Saving linq entity object in C: drive

A

Andrus

I have subclassed entity object used to store settigns per every MDI form:

class MyApp.EntityBase.Kontekst {
public string mybaseprop1 { get { ... } set { ... } }
....
}

class MyApp.EntityExtension.Kontekst: MyApp.EntityBase.Kontekst{
public string property1 { get { ... } set { ... } }
....
}

class MyApp.Business.Kontekst: MyApp.EntityExtension.Kontekst {
public string method1 { ... }
....
}

I need to save / restore it in user computer. I tried to use the following
method to save

public static void Save(object obj, string id) {
IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();
XmlSerializer formatter = new XmlSerializer(obj.GetType(), "mynsp");
.....

but last line causes runtime error

There was an error reflecting type 'Kontekst'.

Types MyApp.EntityExtension.Kontekst' and 'Kontekst' both
use the XML type name, 'Kontekst', from namespace 'mynsp'. Use XML
attributes to specify a unique XML name and/or namespace for the type."


How to fix this ? Kontext classes are Linq classes generated automatically.

Is it possible to use some other form of saving or serialization or should I
add xml
attributes manually ?

Andrus.

..NET 3.5 WinForms.
 
N

Nicholas Paldino [.NET/C# MVP]

Andrus,

Well, the exception message is pretty specific about the resolution to
the issue. Because you use the same class name in different namespaces (XML
serialization doesn't take namespaces into account), you need to add an
XmlTypeAttribute to the classes and specify a different type name for each,
or if the type name is the same, specify a different namespace for each.
^Something^ has to be different between the three Kontekst types.
 
A

Andrus

Nicholas,
Well, the exception message is pretty specific about the resolution to
the issue. Because you use the same class name in different namespaces
(XML serialization doesn't take namespaces into account), you need to add
an XmlTypeAttribute to the classes and specify a different type name for
each, or if the type name is the same, specify a different namespace for
each. ^Something^ has to be different between the three Kontekst types.

thank you.

Kontext classes are autogenerated.
I do'nt like to modify generated code manually since changes are lost if
classes are re-generated.

How to avoid manual changes ?
Is it possible to use DataContrct based or Binary serialization or some
other form to save data?
I need only to save/restore public property values.

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
thank you.

Kontext classes are autogenerated.
I do'nt like to modify generated code manually since changes are lost if
classes are re-generated.

How to avoid manual changes ?
Is it possible to use DataContrct based or Binary serialization or some
other form to save data?
I need only to save/restore public property values.

Are they partial classes? If so, you can apply the XmlTypeAttribute to
another (non-generated) file for the same partial class.
 
A

Andrus

Jon,
Are they partial classes? If so, you can apply the XmlTypeAttribute to
another (non-generated) file for the same partial class.

Thank you. I was able to generate Kontekst types as partial classes and
apply
XmlTypeAttribute .
After that I got the error in this line

Unable to generate a temporary class (result=1).\r\nerror CS0012:
The type 'MyApp.EntityExtension.Kontekst' is defined in an assembly that is
not referenced.
You must add a reference to assembly 'EntityExtension, Version=1.0.0.0,
Culture=neutral,PublicKeyToken=null'.\r\n"

EntityExtension is dynamically generated assembly. It is loaded runtime.
If I use binary formatter (changed line to

IFormatter formatter = new BinaryFormatter(); )

serialization works OK.

How to fix this exception ?

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
Thank you. I was able to generate Kontekst types as partial classes and
apply
XmlTypeAttribute .
After that I got the error in this line

Unable to generate a temporary class (result=1).\r\nerror CS0012:
The type 'MyApp.EntityExtension.Kontekst' is defined in an assembly that is
not referenced.
You must add a reference to assembly 'EntityExtension, Version=1.0.0.0,
Culture=neutral,PublicKeyToken=null'.\r\n"

EntityExtension is dynamically generated assembly. It is loaded runtime.
If I use binary formatter (changed line to

IFormatter formatter = new BinaryFormatter(); )

serialization works OK.

How to fix this exception ?

Hmm. If it's not a strongly named assembly, could you generate a
"dummy" assembly to reference at compile time, then make sure you've
got the real one at execution time? I don't know whether or not that
will work, but it *might*.
 
A

Andrus

Jon,
Hmm. If it's not a strongly named assembly, could you generate a
"dummy" assembly to reference at compile time, then make sure you've
got the real one at execution time? I don't know whether or not that
will work, but it *might*.

I'm currently using this approach.
My solution contains project which produces dummy EntityExtension.dll
In start of Main() I delete this EntityExtension.dll and use AppDomain
AssemblyResolve() to provide new assembly.
Binary serializer and all Linq seems to work OK.

Only XmlSerializer causes this exception.
Maybe XmlSerializer requires assembly to have the same name,
EntityExtension?
I'm using the code below to create assembly in memory at runtime.
How to change this code so that it creates the assembly named
EntityExtension ?
Currently name contains some unique characters different in every time.

Andrus.

public static Assembly CreateAssembly() {

Microsoft.CSharp.CSharpCodeProvider provider = new
Microsoft.CSharp.CSharpCodeProvider
(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });

CompilerParameters compilerParameters = new CompilerParameters();
compilerParameters.GenerateInMemory = true;
compilerParameters.ReferencedAssemblies.Add("DataAccessBase.dll");
compilerParameters.ReferencedAssemblies.Add("Entity.dll");
compilerParameters.ReferencedAssemblies.Add("dblinq.postgresql.dll");
compilerParameters.ReferencedAssemblies.Add("dblinq.dll");
compilerParameters.ReferencedAssemblies.Add("System.dll");
compilerParameters.ReferencedAssemblies.Add(@"c:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.5\System.Data.Linq.dll");
compilerParameters.ReferencedAssemblies.Add(@"c:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.5\System.Core.dll");
compilerParameters.ReferencedAssemblies.Add("System.Data.dll");
compilerParameters.ReferencedAssemblies.Add("System.Xml.dll");
string code = CreateWrapperAssembly();
CompilerResults compilerResults =
provider.CompileAssemblyFromSource(compilerParameters, code);

if (compilerResults.Errors.HasErrors) {
throw new ApplicationException("compile error");
}
return compilerResults.CompiledAssembly;
}
 
J

Jon Skeet [C# MVP]

Andrus said:
I'm currently using this approach.
My solution contains project which produces dummy EntityExtension.dll
In start of Main() I delete this EntityExtension.dll and use AppDomain
AssemblyResolve() to provide new assembly.
Binary serializer and all Linq seems to work OK.

Only XmlSerializer causes this exception.
Maybe XmlSerializer requires assembly to have the same name,
EntityExtension?
I'm using the code below to create assembly in memory at runtime.
How to change this code so that it creates the assembly named
EntityExtension ?
Currently name contains some unique characters different in every time.

Set the OutputAssembly property on the CompilerParameters.

By the way, if you're using C# 3 you should really look at using
object/collection initializers more - you're code would be a *lot*
simpler :)
 
A

Andrus

Jon,
Set the OutputAssembly property on the CompilerParameters.

I tried it but the problem persists. I tried to set it to invalid file name
by using
compilerParameters.OutputAssembly = "\000 ,.<>";

but this does not cause error. So it seems that this parameter is not used
in in-memory assembly generation.

How to force XmlSerializer() to find dynamically loaded assembly ?
By the way, if you're using C# 3 you should really look at using
object/collection initializers more - you're code would be a *lot*
simpler :)

I changed code to

CompilerParameters compilerParameters = new CompilerParameters() {
{ "DataAccessBase.dll" },
{"Entity.dll"},
{"dblinq.postgresql.dll"},
{"dblinq.dll"},
{"System.dll"},
{@"c:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.5\System.Data.Linq.dll"},
{@"c:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.5\System.Core.dll"},
{"System.Data.dll"},
{"System.Xml.dll"} };


but got error
Cannot initialize type 'System.CodeDom.Compiler.CompilerParameters' with a
collection initializer because it does not implement
'System.Collections.IEnumerable'

How I can use object/collection initializers more ?

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
I tried it but the problem persists. I tried to set it to invalid file name
by using
compilerParameters.OutputAssembly = "\000 ,.<>";

but this does not cause error. So it seems that this parameter is not used
in in-memory assembly generation.

That's not necessarily the case. What was the generated assembly name
(when you ask the Assembly object itself) in this case?
How to force XmlSerializer() to find dynamically loaded assembly ?

At this stage it's beyond my knowledge, I'm afraid.
I changed code to

CompilerParameters compilerParameters = new CompilerParameters() {
{ "DataAccessBase.dll" },
{"Entity.dll"},
{"dblinq.postgresql.dll"},
{"dblinq.dll"},
{"System.dll"},
{@"c:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.5\System.Data.Linq.dll"},
{@"c:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.5\System.Core.dll"},
{"System.Data.dll"},
{"System.Xml.dll"} };


but got error
Cannot initialize type 'System.CodeDom.Compiler.CompilerParameters' with a
collection initializer because it does not implement
'System.Collections.IEnumerable'

Indeed. If you look at your original code, you don't try to add strings
to the CompilerParameters directly - you add them to the referenced
assemblies.
How I can use object/collection initializers more ?

// Earlier, on one line:
const string ReferenceBase35 = @"c:\Program Files\Reference Assemblies
\Microsoft\Framework\v3.5\";


CompilerParameters compilerParameters = new CompilerParameters
{
GenerateInMemory = true,
ReferencedAssemblies =
{
"DataAccessBase.dll",
"Entity.dll",
"dblinq.postgresql.dll",
"dblinq.dll",
"System.dll",
ReferenceBase35+"System.Data.Linq.dll",
ReferenceBase35+"System.Core.dll",
"System.Data.dll",
"System.Xml.dll"
}
};

Much clearer, if I do say so myself :)

(Untested, mind you...)
 
A

Andrus

Jon,
That's not necessarily the case. What was the generated assembly name
(when you ask the Assembly object itself) in this case?

I was wrong. It changes the in-memory assembly name.

OutputAssembly = "EntityExtension";

Generates assembly name

CompiledAssembly = {EntityExtension, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null}

However XmlSerializer() call still causes exception. In this exception
assembly name is the same:

Message="Unable to generate a temporary class (result=1).\r\nerror CS0012:
The type 'MyApp.EntityExtension.Kontekst' is defined in an assembly that is
not referenced. You must add a reference to assembly 'EntityExtension,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.\r\n"

So assembly names seems to be same.
At this stage it's beyond my knowledge, I'm afraid.

Exception occurs in

System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns,
XmlSerializerCompilerParameters xmlParameters, Evidence evidence)

So XmlSerializer() uses dynamic compilation and this fails to resolve
dynamically loaded assembly.
// Earlier, on one line:
const string ReferenceBase35 = @"c:\Program Files\Reference Assemblies
\Microsoft\Framework\v3.5\";

Excellent! Thank you very much.
In customer computer framework may be installed to directory than this.

How to determine the location of

c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\

in customer computer ?

Andrus.
 
J

Jon Skeet [C# MVP]

Exception occurs in

System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns,
XmlSerializerCompilerParameters xmlParameters, Evidence evidence)

So XmlSerializer() uses dynamic compilation and this fails to resolve
dynamically loaded assembly.

Mmm... one for more of an XML serialization expert, I'm afraid.
Excellent! Thank you very much.
In customer computer framework may be installed to directory than this.

How to determine the location of

c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\

in customer computer ?

Have a look in the registry at key

HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\v3.5 and value
"All Assemblies In".
 
A

Andrus

Jon,
Have a look in the registry at key

HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\v3.5 and value
"All Assemblies In".

Why those assemblies are not put onto GAC ?
Looks very inconvenient to treat some assemblies differently than others.

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
Why those assemblies are not put onto GAC ?
Looks very inconvenient to treat some assemblies differently than others.

They *are* in the GAC. (Have a look in the GAC to verify - they're
certainly in my GAC.) They're also in the reference assemblies
directory so they're visible as actual files. (This is kinda handy when
it comes to using Reflector, for instance...)
 
A

Andrus

Jon,
They *are* in the GAC. (Have a look in the GAC to verify - they're
certainly in my GAC.)

If so then dynamic compile must find them without directory prefix.

Why those assemblies require using directory prefix to be usable from
dynamic compile ?
They're also in the reference assemblies
directory so they're visible as actual files. (This is kinda handy when
it comes to using Reflector, for instance...)

Why this directory exists and contains only those particular files?
Is it only for Reflector ?
Is so why other assemblies are not provided in this way ?

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
If so then dynamic compile must find them without directory prefix.

I'd have expected it to - at least if you specify the full assembly
name including version.
Why those assemblies require using directory prefix to be usable from
dynamic compile ?

Not sure. Try the full name.
Why this directory exists and contains only those particular files?
Is it only for Reflector ?
Is so why other assemblies are not provided in this way ?

I think they've been moving towards this model rather than keeping them
in c:\Windows\Microsoft.NET\framework\vXXX
 
A

Andrus

Jon,
Not sure. Try the full name.

http://msdn2.microsoft.com/en-us/library/s5bac5fx.aspx describes that
reference directory is *not* included in compiler default search
directories:

The compiler searches for assembly references that are not fully qualified
in the following order:

1.. Current working directory. This is the directory from which the
compiler is invoked.
2.. The common language runtime system directory.
3.. Directories specified by /lib.
4.. Directories specified by the LIB environment variable

It seems that they forget to include reference directory to compiler default
search path in 3.5.

Andrus.
 
A

Andrus

Jon,
Not sure. Try the full name.

http://msdn2.microsoft.com/en-us/library/s5bac5fx.aspx describes that
reference directory is *not* included in compiler default search
directories:

The compiler searches for assembly references that are not fully qualified
in the following order:

1.. Current working directory. This is the directory from which the
compiler is invoked.
2.. The common language runtime system directory.
3.. Directories specified by /lib.
4.. Directories specified by the LIB environment variable

It seems that they forget to include reference directory to compiler default
search path in 3.5.

Can we put this to wishlist ?

Andrus.
 
J

Jon Skeet [C# MVP]

Andrus said:
Jon,


http://msdn2.microsoft.com/en-us/library/s5bac5fx.aspx describes that
reference directory is *not* included in compiler default search
directories:

The compiler searches for assembly references that are not fully qualified
in the following order:

1.. Current working directory. This is the directory from which the
compiler is invoked.
2.. The common language runtime system directory.
3.. Directories specified by /lib.
4.. Directories specified by the LIB environment variable

It seems that they forget to include reference directory to compiler default
search path in 3.5.

Can we put this to wishlist ?

I don't think the reference directory is *really* meant for that
purposee. You'd be better off loading it from the GAC with the full
name, IMO.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top