CodeDom extensions and dynamic LINQ (string/script to LINQ emitting)
Posted by Igor Moochnick on 01/11/2008
Ah… Don’t you like these method extensions?!!!
Recently I’ve developed a certain taste for method extensions. They allow to create shorter code, but you should always keep in your head the dangers of such freedom – code can become complicated and unnatural. On the other hand, new set of extensions, it’s like to learn a new language, new classes, new techniques. So, currently, I have a mixed feeling – great thing, but not for free (some overhead attached).
In my previous post (Intellisense-like method selection pop-up window) I’ve shown how to create a LINQ editor (or any editor of your choice). You can ask – what’s the point of such script? What can we do with it?
.NET answer – the sky is your limit. The .NET framework allows you dynamically convert any string (correctly formatted) into a code and immediately execute it. You can do this in-memory, in-file, from-file, to-file, from-string, to-string, and any other combination of your choice.
Here are a couple of example of what you can do with a correctly formatted string:
Generate a full code file
Generate an assembly in-memory
Create an assembly in file for future use
As well you have a wide choice of code providers:
and any other code provider that is installed on your machine and inherits from System.CodeDom.Compiler.CodeDomProvider class.
To simplify the use of CodeDom (it requires a wide knowledge of how the build process works), I’ve created a very basic library that extends CodeDom functionality and provides a nice and slick way of generating code-on-the-fly. Here are a couple of small examples, before we’ll dive into how it’s done (find them in the attached test project):
A) The following snippet prints Hello World! to the console:
B) And here is the example of how to call a function with parametes (it’ll add 2 numbers):
C) and here is the BEST – dynamic LINQ (compiled and executed in memory):
Obviously you can start mix and match these extensions and create more namespaces, classes with more methods, etc … I don’t want to get too much into the details (see attached test project for more samples), but rather I want to tell how it works.
NOTE: Yes, I know about the Dynamic LINQ library and I’ve read ScottGu’s blog article, but, so far, I’ve seen only that you can create filters with this technique. It is using tokenized parser behind the scenes and reflects expression tree based on the results. This has somewhat limited functionality and can’t emit expression tree solely based on a string LINQ query. May be in the future it’ll be expanded, but, at this moment, my solution is the only way to convert LINQ string to an executable code.
Extension methods are the key. CodeDom class just provides a set of factories to create the CodeDom classes like CompileUnit, CodeTypeDeclaration, CodeSnippetTypeMember, etc… It collects all the namespaces, referenced assemblies, constructed code and, at the end, it allows you to decide what is a final product of your actions. The beauty of the code ensured by the extension methods.
I will not go too much into details of how to create extension methods themselves (a lot of information can be found on the net), but rather talk about the most important one: Method.Invoke.
This extension method makes the constructs like “method.Invoke()” and “method.Invoke(2, 3)” possible. It hides the complexity of the reflection from the users and allows him to use the newly constructed method as if it’s a real method of the application.
At the end I want to mention, yet another, beauty of .NET – dynamic type converter. Such converter allows you to take control over your untyped objects without second-guessing your type conversion assumptions and skipping all the error check logic. I’ve used such technique very extensively with untyped DataSets in all my middle tier applications (I’ll be posting about it soon). For example, Method.Invoke always returns an object. If you want to use the returned value as a strongly typed object – you have to cast it to the required type either by “()” or “as” constructs. Then you need to check for a “null” or an exception. In my case you can do something like this:
Note the last “0” in the converter’s call – this is a default value that will be returned in case the conversion has failed. This is how it works:
I’ll be talking about this converter in more detail in the future posts, so stay tuned …
DISCLAMER: The provided libraries are a work in progress and do not contain 100% wrapped CodeDom. There are a lot of things that can be added to make the whole user experience easier. You are welcome to use them AS-IS and to extend them to any degree you with. You’re welcome to send me your updates and I’ll be more than happy to post them for everybody else to use.