In Jimmy Nilsson’s book “Applying Domain Driven Design and Patterns” he writes about a project of his called “NWorkspace”, which is a kind of abstraction layer for O/R mappers.
The concept is to let your Repositories only talk to the workspace abstraction, making it possible to swap underlying mapper.
The biggest problem with such abstraction is querying, there have never existed a standard way to make queries for domain models in .NET.
So you end up abstracting the querying mechanism too, and that in turn forces you to create translators from your own query model to the underlying mappers query model.
Now days we have Linq.
Linq allows us to create queries in a standardized way against domain models.
So, yesterday I decided to try to make a Linq based abstraction layer in the spirit of NWorkspace.
(Note: My project is simply a ripoff from Jimmy’s concept, there is no relation to the real NWorkspace)
First I had to pick some different persistance tools, so I decided to go with NHibernate, LinqToSql and DB4O (object DB for .NET)
Once I had selected the persistence tools I started writing my abstraction layer.
It turned out to be really easy to implement, one interface with five methods:
LinqQuery[of T] //creates a Linq query
Add(T) //attaches a transient entity to the workspace
Delete(T) //removes a persistent entity from the workspace
Commit() //commits all changes to the persistent store
Rollback() //discards all changes
I then created an implementation for each of the persistence tools for my interface.
So now I can pass instances of my workspace into my repositories and make them operate almost agnostic of what persistence tool I use.
I say “almost” because there are a bit too many differences between the tools when it comes to query support and entity design.
LinqToSql is far from being POCO / PI, and even if NH and DB4O are both POCO’ish, there are still differences.
There are also differences in the Linq support, so you can not always run the same type of queries against the different workspace implementations.
But all in all, it turned out really nice, making the Repository design much cleaner.
As long as you adapt your domain model for whatever persistence tool you use and don’t use too complex queries, the most of your repositories and business logic can remain unchanged.
Pretty slick IMO.
I am afraid that abstracting away the OR mapper and making your application work with multiple mappers can be very restricting, as you say there are very large differences both in how they work and in what features that have. If I use NHibernate for example I want to be able to take advantage of it’s advanced features.
But the idea is very interesting, especially if different entites can use different implementations of the “Workspace”. Then you could query entites through a common interface where some have entites are fetched and saved by nhibernate and other entites that are fetch and saved via xml files or via web services. Maybe such an abstraction would be a little leaky though :)
Interesting. Will you make your project available to the general public? You can host it here: http://projects.db4o.com if you want.
What was your experience with trying db4o?
db4o community manager
Very cool. I’d like to see Entity Framework and LLBLGen support added ;-).
But seriously, LINQ to SQL has this annoying limitation; It can only talk with MS SQL Databases. I’d like to see some guidance from Microsoft that tells us how to write our LINQ to SQL code in such a way, that we later can easily upgrade to the Entity Framework. An abstraction like this could help.
I agree that such abstraction might give you the least common denominator for all mappers.
But the real purpose is not quite to do this.
In Jimmy’s case which he writes about in his book it was more a case of swapping between a NHibernate workspace and a Unit test workspace, thus removing NH from the equation while running tests.
And I _know_ it is impossible to make a 100% transparent abstraction due to the different behaviours and entity design of the mappers.
Eg, LinqToSql uses EntitySet of T for list properties.
So any linq sub query touching a list property will use compile time binding to EntitySet of T.
So there is no way at all to make this work w/o recompiling your repositiroeis , even if the actual source code is not changed..
(Linq uses a sort of Compile time Ducktyping, using the _worst_ of two worlds.. thus, the query will bind directly to concrete implementations of sources and making it impossible to run the same query over other sources… )
But what this kind of abstraction _can_ bring to the table is to allow you to work with different engines in a similair way, thus decreasing the learning curve when using a new engine.
And: it will allow you to port an application to a new engine w/o rewriting and restructuring all of it.
So this will not get us into Dependency Injection Heaven, but atleast it will at least keep us from _rewriting all of it_ hell
At the moment the project is just a small experiment so there is not much to show to the world.
But I will continue to fiddle with it now during my vacation, so if I feel confident enough in it I will make it public.
But for now it will just be my own toy :-)
Regarding my experience with DB4O:
I have only browsed the examples and played a bit with it in my workspace project so far.
But to sum up what I have seen so far:
It is way way way easier to set up and get going than any O/R mapper solution out there.
I was able to store and retreive objects on my first try w/o ever touching DB4O before.
I have felt some confusion on class schema evolution thogh,
But I guess that is simply because everything else have been so smooth that I havent had to read any documentation yet.
When it comes to an O/R mapper I can easily check what sql gets sent to the DB and whats the content of the DB.
I feel somewhat lost without that.
>> LinqToSql is far from being POCO / PI
I’ll have to correct you on this one, Roger. When you use the true-PI-checklist from Jimmy Nilsson’s book, you can actually see that LINQ to SQL can be very POCO’ish (and does a better job than Entity Framework v1.0). Vijay P. Metha used Jimmy’s checklist in his book ‘Pro LINQ Object Relational Mapping with C# 2008’. LINQ to SQL doesn’t comply in the following ways:
1. Special data types are needed (EntitySet and EntityRef)
2. You are required to have a default constructor.
3. Specific interfaces need to be implemented (but only IQueryable, so not big of a deal because all OR/M tools that want to support LINQ must implement this).
LINQ to SQL passes all other tests in Jimmy’s checklist.
Well I guess the definition of POCO is not written in stone.
I do not consider neither NPersist or NHibernate to be fully POCO.
And Linq to Sql is in my oppinion far from POCO.
as soon as you require the use of special types, you are not even close to POCO in my world.
Also, you are wrong on part 3)
You can implement your own linq provider w/o touching IQueryable interface.
We did this in NPersist.