In the previous posts I showed you how to write LINQPad drivers and how to read the meta data out of db4o. This time I show the last piece for a functional driver, the code generation. Code generation is required to dynamically create the query context and entities for LINQPad.
Side note: I’ve fixed more bugs in the driver. I’ve also added the code to access meta-information. However that part doesn’t work at the moment due to db4o-bugs.
Code Generation Basics
There are different ways to generate code in .NET. The low level Reflection.Emit namespace gives you basic code generation capabilities. With the CodeDom-API you can compile regular C# code at runtime. Also remember that you can use LINQ-Expressions to create delegate implementations. Of course there are numerous external libraries which help, like Castle Dynamic Proxies.
For my LINQPad driver I’m using the basic Reflection.Emit API. The first step you need to do is to create a new assembly which hosts the code:
After that you can create any numbers of new types:
You can add fields, properties and methods to the created types.
The hardest part about is to fill the method-body. For that you generate CIL code. That’s loads of fun. If you’re not familiar with CIL code you maybe should at the CIL-code from existing assemblies to learn from it.
The last step is to create the type. This freezes the type and it can be loaded by the CLR.
That’s it; you’ve generated code at runtime. You now go crazy with it =).
Mixing with Expressions
As we’ve seen generating code directly with CIL expressions is painful. A more elegant way is to compose LINQ-Expressions at runtime and the compile these expressions. You actually can generate a method body with expressions. The LambdaExpression has a compile method which accepts a method builder (since .NET 4.0) . That makes it much easier to implement the method body:
However there’s a huge limitation to this. You can only generate code for static method right now. =/
Generating Code for LINQPad
LINQPad driver needs two things. The first is types for the entities which you want to query. In my driver I generate classes for all stored types in db4o. The second thing is the query context. The query context should have a structure like this:
LINQPad takes that query-context and creates a subclass of it. That subclass contains an enty-method which contains the code typed into LINQPad. That way your query simply can access all the properties of your database context. Of course you can add any method or property to your context and can make it accessible from LINQPad.
Well know we know all important steps to write a LINQPad driver, from the LINQPad API to the code generation. Have fun writing your own driver or using an existing driver.
- Making of the db4o LINQPad Driver: Meta-Data