Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Antler: Abstraction over ORM that you like to use in .NET(Part l)

4.77/5 (15 votes)
5 Aug 2014CPOL4 min read 29.8K   163  
Using the same syntax to work with different ORMs.

Image 1

Introduction

At some point we realized that developing various .NET projects we had been repeating similar things from time to time. Usually our projects have the following commonalities:

  • Use some ORM to work with database in Code First style.
  • Use some IoC container.
  • Use UnitOfWork/Repository patterns in one way or another.
  • Have decoupled architecture and good testability.

As a result, we decided to implement fully pluggable open-source framework to work with popular ORMs using the same syntax.

Now this framework is used by several major organizations, including UBS Investment Bank. May be someone else will find it useful or even will have interest to contribute to it.

Please consider this article as high-level introduction to the Antler framework.

Framework's goals

  • To use common syntax to work with different ORMs, so we could easily substitute one ORM with another.
  • To have very easy "fluent" configuration.
  • To be fully pluggable. For example, it should be damn easy to choose which ORM, IoC container or database to use.
  • Applications that use framework should be easily testable(unit & integration tests).

Concepts explained

Abstraction over ORM

Core idea is to have unified syntax to work with any ORM. This is accomplished through implementation of UnitOfWork, Generic Repository etc. patterns in fully decoupled style.

Unified syntax provides an ability to switch between different ORMs without any pain: you just need to update one configuration line in application’s bootstrapper and update mappings for your entities. Even if you are not planning to switch ORMs in future, this ORM-agnostic architecture makes your code cleaner.

Configuration is trivial, the following example shows how to configure application to use EntityFramework ORM and Castle Windsor container:

C#
var configurator = new AntlerConfigurator();
configurator.UseWindsorContainer()
            .UseStorage(EntityFrameworkStorage.Use.WithConnectionString(connectionString).
                        WithMappings(assemblyMithMappings)); 

Then you could use the unified syntax to work with your database. For example, inserting in database:

C#
UnitOfWork.Do(uow =>
                { 
                  uow.Repo<Team>().Insert(new Team() {Name = "Penguins", Description = "Hockey"});
                  uow.Repo<Team>().Insert(new Team() {Name = "Capitals", Description = "Hockey"});
                  uow.Repo<Team>().Insert(new Team() {Name = "Nets", Description = "Basketball"});
                });   

Querying from database:

C#
var found  = UnitOfWork.Do(uow => uow.Repo<Team>().AsQueryable().
                                                   Where(t => t.Description == "Hockey").
                                                   OrderBy(t => t.Name).ToArray());  

UnitOfWork here represents wrapper around underlying database's transaction. Generic Repository provides IQueryable interface to query database + set of standard operations like Insert, Delete etc.

But if for some reasons you need to use ORM specific syntax, of course, you could reach “internal” ORM's session. You may need this to perform something specific with ORM’s session that does not fit to the unified Antler syntax.

For example, you can reach “internal” NHibernate session ISession and use NHibernate's QueryOver method as follows:

C#
UnitOfWork.Do(uow =>
                  {
                    var internalSession = uow.SessionScope.GetInternal<ISession>();
                    var result = internalSession.QueryOver<Team>().Where(t => t.Name == "Awesome")
                                                                  .List();
                     //do something with result
                   });   

Currently, NHibernate, EntityFramework and Linq2Db ORMs are supported by Antler framework.

Abstraction over IoC

IoC containers also have their abstraction in Antler framework. After installing your favorite container in application’s bootstrapper there is no dependency on it in your code. All work with container is carried out via IContainer interface that looks like:

C#
public interface IContainer
 {        
    T Get<T>();        
    T Get<T>(string name);        
    objectGet(Type type);        
    object Get(Type type, string name);        
    IList<T> GetAll<T>();        
    IList GetAll(Type type);        
    void Release(object instance);        
    void Put(IBindingSyntax binding);        
    bool Has<T>();        
    bool Has(Type type);        
    bool Has<T>(string name);        
    bool Has(Type type, string name);
  } 

The Following example shows how to configure application to use StructureMap container and NHibernate + SqlServer:

C#
var configurator = new AntlerConfigurator();
configurator.UseStructureMapContainer()
            .UseStorage(NHibernateStorage.Use.WithDatabaseConfiguration(MsSqlConfiguration.
                        MsSql2008.ConnectionString(connectionString)).
                        WithMappings(assemblyWithMappings));  

Currently, Castle Windsor and StructureMap IoC containers are supported in Antler framework.

By the way, my thanks to Kostassoid for this part.

Testability

As said before, goal of Antler framework is to provide the ability to switch between ORMs, IoCs and databases very easily. That is very useful when writing integration tests for your functionality, because you could configure your testing environment trivially.

For example, if you use NHibernate + Oracle in your application you may have the following configuration in bootstrapper:

C#
var configurator = new AntlerConfigurator();
configurator.UseWindsorContainer()
            .UseStorage(NHibernateStorage.Use.WithDatabaseConfiguration(
                        OracleDataClientConfiguration.Oracle10.ConnectionString(connectionString).
                        DefaultSchema(dbSchemaName)).WithMappings(assemblyWithMappings)); 

And then you could decide not to create another heavy Oracle database for integration testing. Instead, you could configure your testing project to use NHibernate + Sqlite in-memory database. In this case your testing project will use the same ORM mappings that your application, but different database:

C#
var configurator = new AntlerConfigurator();
configurator.UseWindsorContainer()
            .UseStorage(NHibernateStorage.Use.WithDatabaseConfiguration(SQLiteConfiguration.
                        Standard.InMemory()).WithMappings(assemblyWithMappings)); 

Pluggable structure

Image 2

Framework implemented in a “pluggable” style. Antler.Core project contains all abstractions and shared functionality. And there are other projects(adapters) with concrete implementations. For example, adapter for NHibernate ORM or Castle Windsor IoC container.

So, if we’d like to use NHibernate as ORM and Castle Windsor as IoC container in our project, then we need to install Antler.Core main library and Antler.NHibernate/Antler.Windsor adapters from NuGet. Then if we will decide to change ORM, for example, from NHibernate to EntityFramework, then we just need to replace Antler.NHibernate adapter with Antler.EntityFramework from NuGet.

Similarly, we can change IoC container adapter(for example, from Antler.Castle to Antler.StructureMap) if we’ll need so.

Currently, there are ORM adapters for NHibernate, EntityFramework, Linq2Db and IoC adapters for Castle Windsor and StructureMap.

Conclusion

Despite on production usage of Antler framework, there are a lot of work ahead. We have plans to add adapters for other popular IoC containers and ambitious idea to adapt Antler framework for NoSql solutions(MongoDb etc).

Contribution is really appreciated. If you are interested, project's page on GitHub here.

If you would like just to try this framework, you could easily install it from NuGet:

Core library, and adapters for NHibernate, EntityFramework, Linq2DbCastle Windsor, StructureMap.

If you will have problems with installing/using it, or any questions, please let me know. Thank you for your attention.

Continue reading(Part ||)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)