Apr 9, 2008

A Linq playground for you and your objects

In this post we'll implement a very basic Linq Console application that supports "ad hoc" queries allowing users to enter Linq queries and run them against a db4o database. Our main goal is to show how such mechanism can be implemented on top of Linq/Db4o.

That said, let's play with the application for a while and them dig a little bit in its code.

How to use it ?

First, download the code here and compile it (any version of Visual Studio 2008 can be used).

Now populate a db4o database (with data) and copy the an assembly containing the types stored in that database to the LinqConsole.exe folder.


You will need to copy your model assembly to
LinqConsole executable directory, otherwise it
will not be able to find it.


Start the application and, once presented the prompt (
>), enter commands (prefixed with ":") or a query (finished with ";"). The very first command you may want to try is :help to see a list of valid commands (type :help and press ):
Basically, we want to run a (Linq) query against a db4o database, so we need to specify:
  • the database file to query (through database command)

  • the assembly containing the types stored in the database; we call this assembly, model assembly because usually, it will hold your business objects (the model in MVC pattern). Unfortunately, this assembly must be in the same directory as the application, otherwise the application will not be it able to find it (technically one can configure the application using Linq.Console.exe.config file to allow assemblies to be stored in alternative directories).
With these parameters defined you can type Linq queries as you would do pragmatically, for instance:

from Type t in database select t;

and press to see the results:


As the application tries to run the query only when it finds a semicolon ";" it's also possible to enter a query in multiple lines. You can see a short demo of the application in bellow.








Before leaving this topic, I'd like to call your attention that in order to Linq for db4o to work you need to specify a type in the query (that's the entry point for our custom "Linq Provider"), otherwise, you will get an error like the one bellow:


Note that in this case I've omitted Person type in the query, i.e, I've typed:

from p in database select p;

instead of:

from Person p in database select p;


How it works ?

The basic idea behind this application is pretty simple: Given a model assembly (specified through
load command), a db4o database (specified through database command) and a Linq query, find all types and namespaces defined in such assembly them emit code to run the query against the database.

In
CompileQuery() method we emit a new type (TheQuery) using the specified values (database, namespace and query). This type implements IInteractiveQuery interface, so, after instantiating it we call its Run() method to execute the query.

In order to dynamically compile this type (
TheQuery) we use classes defined in System.CodeDom.Compiler and Microsoft.CSharp namespaces; any compilation errors will be rendered in red on yellow background text.

Improvements

Well, I must admit: in its current state, this program has some limitations (but most of them are there to allow you to play with it ;)

From the top of my head, I can came up with a list (of coarse, not an exhaustive one) of possible improvements:
  • Support model assemblies in other directories
    Currently in order to use a model assembly, users are required to copy it to the application folder.


  • Check if the types stored in the database can be found in the model assembly.

  • Better error handling in general

  • Warn users about invalid commands Currently nothing happens for invalid commands (no message).

  • List types inside an assembly A new command (for instance, types) could be added to allow users to list the types contained in an assembly.

  • Add support to query Db4o in server mode.

  • Auto-completion This probably would require major changes in the input handling, but it would be nice to add auto-completion to list types, assemblies, databases, etc. in the commands.

  • You name it! The sky is the limit :)
Final Words!

I really expect that you enjoyed this application; if you have questions/suggestions please, fell free to drop me a line :)

Thoughts?

Adriano

4 comments:

Anonymous said...

Does Linq work properly with TP? I have seen problems some weeks ago.

Vagaus said...

Hi,

There was problems (exceptions) with Linq and TP but only with instrumented assemblies (hand implemented TP should not be a problem).

In the latest builds the problem was relieved by falling back to Linq to Objects in these situations (i.e, the query would run unoptimized).

We are working to fix the problem as soon as possible. Progress can be tracked here.

Adriano

Anonymous said...

It seems that it's safe to use it now (speed doesn't matters at the moment). Thanks, Adriano

Vagaus said...

You are welcome :)

Let us know if you find any problem.

Adriano