Parsing Linq Queries as String with CodeDOM

INTRODUCTION

I recently worked on very interesting and challenging project. It was about parsing the dynamic queries that were created by the user as strings. These queries were actual the Linq queries. Initially I thought that it is not possible but then after scratching my head and doing some binging . I came out with a great solution which was satisfying my client needs. Hence I decided to share this achievement of mine with the community as a result of which I am writing this post. I will show you that how you can do a dynamic compilation of any CLR compliant code which is in string format and get the desired output.

CodeDOM

.NET Framework has so much to offer that few of its namespaces gets unnoticed by developer. System.CodeDOM is one of them. CodeDom is the abbreviation and it stands for Code Document Object Model. This namespace has the rich set of the classes which makes the coding a breeze.The namespace includes all the programming aspects classes like Declarations,Parameteres,ClassCreation etc. I won’t get into the details of these classes as it is vast topic within itself. Let us jump into the solution and see some code. I have very simple linq query code which is shown below.


int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numbersplusone = (from n in numbers
select n + 1).ToList();
return numbersplusone;

The above code is pretty simple linq which queries the integer array and add one to all its elements and returns the output. As CodeDom doesn’t have the classes yet which can parse the Linq queries. I have to pass the code as a string to the CodeProvider class.
I create a StringBuilder object variable and appends my each code line to it. The compiler class object then compiles the code and generates the assembly in memory. Further using the Reflection classes I derived the type of the generated assembly and invoke the function which returns me the value. Enough of talking now let us see some code in action.

static object DynamicCompilation(string Code)
{
string functionName = "myFunction";

StringBuilder DynamicCodeToCompile = new StringBuilder();

DynamicCodeToCompile.Append("using System;");

DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("using System.Collections.Generic;");
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("using System.Linq;");
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("namespace CodeTest");
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("{");
DynamicCodeToCompile.Append("class EvaluateStringLinq");
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("{");
DynamicCodeToCompile.AppendFormat("public static object {0}()", functionName);
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("{");
DynamicCodeToCompile.Append(Code);
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("}"); //function end
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("}"); //class end
DynamicCodeToCompile.AppendLine();
DynamicCodeToCompile.Append("}"); //namespace end

CodeDomProvider provider;
string language = "cs";
// Check for a provider corresponding to the input language.
if (CodeDomProvider.IsDefinedLanguage(language ))
{
// provider = CodeDomProvider.CreateProvider(language);
provider =new Microsoft.CSharp.CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v3.5" } });

//// Get the compiler settings for this language.

CompilerInfo langCompilerInfo = CodeDomProvider.GetCompilerInfo(language);
CompilerParameters langCompilerConfig = langCompilerInfo.CreateDefaultCompilerParameters();
langCompilerConfig.ReferencedAssemblies.Add("System.dll");
langCompilerConfig.ReferencedAssemblies.Add("System.Core.dll");
langCompilerConfig.ReferencedAssemblies.Add("System.Data.dll");
langCompilerConfig.ReferencedAssemblies.Add("System.Data.Linq.dll");
langCompilerConfig.GenerateInMemory = true;
langCompilerConfig.TreatWarningsAsErrors = false;

CompilerResults results = provider.CompileAssemblyFromSource(langCompilerConfig, DynamicCodeToCompile.ToString());
if (results.Errors.Count > 0)
{
Console.WriteLine("Errors Occured" + results.Errors.Count);
return null;
}

else
{
Type dynamicType;
dynamicType = results.CompiledAssembly.GetType("CodeTest.EvaluateStringLinq");
MethodInfo method;
method = dynamicType.GetMethod(functionName);
return method.Invoke(null, null);
}

}
else
{
// Tell the user that the language provider was not found.
Console.WriteLine("There is no support for input language \"{0}\".",
language);
return null;
}

}

CLOSURE

Lot of dynamism is coming now in modern apps. You need to think out of the box to provide the solutions to your clients. I have barely scratch the surface of the CodeDom namespace but this technology has lot to offer. Imagine you can put your entire business rules expressions in some durabale store and evaluate those expressions at runtime with rich object model of this namespace. I think sky is the limit. Download the sample code from here.
Cheers and Happy Coding ….

Advertisements

8 thoughts on “Parsing Linq Queries as String with CodeDOM

  1. Thanks for uploading the code! It was extremely useful to me! I was able to make user specified dynamic Linq queries rather quickly.

    I’m also impressed with your attention to detail and the fast reply.

    Thanks Again

  2. when i use the above code i m getting ERROR:CS0006 Metadata file ‘System.Core.dll’ could not be found. could you please help me to solve this error

    • Hi Altaf
      I would recommend that you reinstall .net framework. The assembly that you are mentioning is missing. May be reinstallation can solve the issue.

      Cheers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s