CodeDomProvider code generation doesn't work with specific Linq syntax
I am using CodeDomProvider to compile some Linq code and run queries dynamically. However, I am facing a very strange problem.
If my Linq query in the generated code looks like this, everything works:
namespace Dynamic
{
using System.Linq;
using System.Collections.Generic;
public static class Query
{
public static int GetRecords()
{
MyData.Data.DataMart container = new MyData.Data.DataMart();
return (container.EventDetails).Count();
}
}
}
This compiles and works fine. However, if I change the linq query to the following, then it won't compile:
return (from e in container.EventDetails select e).Count();
It works fine if I put it as static code, but if I try to compile it using the CodeDomProvider it doesn't work (and I haven't found a good method for getting error messages why it doesn't work). I would like to use the from-in-select syntax style as it will make it easier for me to generate linq queries, but I cannot figure out why they are not compiling.
You can see some of the code I'm using to compile this snippet from the link at the top of this post.
Thank!
Edit: Copying the code from the post linked to:
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();
cp.GenerateInMemory = true;
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
cp.ReferencedAssemblies.Add("System.Data.Linq.dll");
cp.ReferencedAssemblies.Add("System.Data.Entity.dll");
cp.ReferencedAssemblies.Add("MyApp.Data.dll");
var results = provider.CompileAssemblyFromSource(cp, source);
var assm = results.CompiledAssembly;
Edit2: Regarding the exception, I am getting the exception from the second line to the last line of code (var results = ...). The exception is BadImageFormatException:
Could not load file or assembly '0 bytes loaded from the system, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089' or one of its dependencies. An attempt was made to load a program using an invalid format
source to share
I didn't find an answer how to get good information about exceptions, however I solved this problem. The class library containing the compiler code above was installed in AnyCpu, but the context it was running in ASP.Net was x86. So it was causing it to crash when trying to load System.dll from the moment the wrong version (or something stupid) loaded.
I'll gladly give someone else a checkmark for the answer if you can (a) figure out how to get the real error message, or (b) load the correct reference type.
source to share
This seems to work for me:
static void Main(string[] args)
{
string sourceCode = @"namespace Dynamic {
using System.Linq;
using System.Collections.Generic;
public static class Query
{
public static int GetRecords()
{
MyApp.Data.DataMart container = new MyApp.Data.DataMart();
//return (container.EventDetails).Count();
return (from e in container.EventDetails select e).Count();
}
} }";
string sDynamDll = "Dynamic.dll";
string sDynamClass = "Query";
string sDynamMethod = "GetRecords";
System.CodeDom.Compiler.CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
cp.OutputAssembly = sDynamDll;
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
cp.ReferencedAssemblies.Add("System.Data.Linq.dll");
cp.ReferencedAssemblies.Add("System.Data.Entity.dll");
cp.ReferencedAssemblies.Add("MyApp.Data.dll");
var providerOptions = new Dictionary<string, string>();
providerOptions.Add("CompilerVersion", "v4.0");
CodeDomProvider compiler = CodeDomProvider.CreateProvider("C#", providerOptions);
CompilerResults cr = compiler.CompileAssemblyFromSource(cp, sourceCode);
if (cr.Errors.HasErrors)
{
StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
foreach (CompilerError error in cr.Errors)
{
errors.AppendFormat("Line {0},{1}\t: {2}\n", error.Line, error.Column, error.ErrorText);
}
}
// verify assembly
Assembly theDllAssembly = null;
if (cp.GenerateInMemory)
theDllAssembly = cr.CompiledAssembly;
else
theDllAssembly = Assembly.LoadFrom(sDynamDll);
Type theClassType = theDllAssembly.GetType(sDynamClass);
foreach (Type type in theDllAssembly.GetTypes())
{
if (type.IsClass == true)
{
if (type.FullName.EndsWith("." + sDynamClass))
{
theClassType = type;
break;
}
}
}
// invoke the method
if (theClassType != null)
{
object[] method_args = new object[] { };
Object rslt = theClassType.InvokeMember(
sDynamMethod,
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
null, // for static class
method_args);
Console.WriteLine("Results are: " + rslt.ToString());
}
Console.ReadKey();
}
source to share
You probably get BadImageFormatException
because your code doesn't actually compile into a valid assembly. Perhaps this is because the old 2.0 compiler is used by default. Check out the link below for enabling C # version 3.5 (I don't know if 4.0 is supported, but you don't need it):
http://blogs.msdn.com/b/lukeh/archive/2007/07/11/c-3-0-and-codedom.aspx
Also check the collection Errors
for CompilerResult
which is returned from the method CompileAssemblyFromSource()
. A compilation error does not throw an exception, you must manually check for compilation errors.
source to share