C# has extension methods, and together with lambdas they provide an incredible amount of power in our hands.
They way extension methods are implemented is very smart and makes them measy to write new ones, and even better, they provide a superior ide integration.
That’s all fantastic, but sometime i’ve found myself in the situationwhere i have to Extend the extension methods.
Let me explain my problem: consider a a program that makes use of linq over the abstraction of the unit of work. That is really really cool because if you write your generic UOW, you are basically decoupling your logic from the hard implementation of the database.
This saved me one time already, with this system i was able to switch from Oracle to SqlServer in minutes, literally, without chaning any of my business logic code.
That was easy because i was using NHibernate that already implements query providers for all the databases and dialects that i was interested to use at that time.
Now I have a new problem. I want to have the ability to switch between RavenDb and ordinary Sql databases.
Obviously the query provider are different, and i want to have the ability to switch without hard modifying the dependencies of my project
i want to depend al my code just on my persistence layer, and this in turn have to map to the correct provider
I also want to not change the persistence code to accomodate a new provider, basically i want to plug insupport for new query provider without recompiling, at the extreme i just want to drop a dll in a folder and set up a new connection string in the config file.
Possible? Yes!
i’ve wrote a little helper project based on MEF that discovers new mapper and provides usitilities to plug them into your own set of extension methods, so all your code uniquelly depends by your persistence layer with no dependencies to particular implementations of the db.
It will up to you to provide the correct mapping for all the extension methods you want to support, or simulate them (in case of async method for example) or throw exceptions and handle the situation in your business logic code.
the trick is simple, let’s say i want to implement the SingleOrDefaultAsync method for all the provider i want to support.
I want to just write something like this
public static class QueryableAsyncExtensions
{
public static Task<TSource> SingleOrDefaultAsync<TSource>(this IQueryable<TSource> source)
{
return Extensions
.Of<IQueryableAsyncExtensions>(source.Provider.GetType())
.SingleOrDefaultAsync<TSource>(source);
}
}
and i want that the system automagically find outs all the mapper that implements IQueryableAsyncExtensions and for these one, pick the one that has the same provider of source.Provider
finally on this it will have to invoke the method i want as extension method, in this case SingleOrDefaultAsync<TSource>(source)
the implementation maybe something like
[Export (typeof(IQueryableAsyncExtensions))]
[ExtensionMetadata(Provider=typeof(IRavenQueryProvider))]
class TestExtensionForQueryableAsyncBool : IQueryableAsyncExtensions
{
public Task<TSource> SingleOrDefaultAsync<TSource>(IQueryable<TSource> source)
{
//Call the native RavenDb’s SingleOrDefaultAsync here
}
}
Note that you have two attributes that decorate this class: one is the usual MEF Eport attribute, the second tells to the library for which provider this extension implementation should be used for
Tags: