Mar 16, 2010

Linq in a Generic Data Access Layer with EF4

Introduction

Decoupling a data access layer (DAL) from business or service logic has become mainstream for some time now – not only due to testing (mocking) reasons but also because replacing your ageing data access technology or your database (manufacturer) may be a topic.

The Interface

I’ve created a simple repository pattern (CRUD) kind of interface over the last year or two. The methods of the interface are generic

int Add<T>(T entity);

int Delete<T>(T entity);

int Update<T>(T entity);

IList<T> GetAll<T>();

// and more

The implementation was done with NHibernate 2.1 and recently with Entity Framework 4.0 (EF4).

The Domain Model

I had several domain models and object-relational mapping (ORM) definitions. Here’s a simple model to show things:

image

A blog or facebook like system that stores Entry objects allows for people to add Comments or just say they like it (Liker).

Using Linq 

Linq is a powerful language feature that can be used to submit queries from the layers on top of the DAL. It is data access and database independent because it works on the domain model – so no coupling to a particular technology.

For this reason I provided a Linq entry point to the interface. Shown here is the EF4 implementation:

public IQueryable<T> GetLinq<T>()

{

    ObjectContext context = GetObjectContext();

    return context.CreateObjectSet<T>() as IQueryable<T>;

}

Or even easier with NHibernate and the NHibernate Linq provider:

public IQueryable<T> GetLinq<T>()

{

    return this.Session.Linq<T>();

}

Now you can program statements like

l = databaseContext.GetLinq<Entry>().Where(entry => entry.Title.Length > 5).ToList();

emitting SQLExpress 2008 T-SQL:

image

Or more complex

var result = from x in this.databaseContext.GetLinq<Entry>()

             join y in this.databaseContext.GetLinq<Liker>()

             on x.Id equals y.Entry_FK

             select new { x.Title, y.Posted };

producing

image

Clean and optimized! The above seems to work due to lazy evaluation and loading.

Note that you could use GetAll<T>().Where() kind of code, but this would return all (10000+) records and only filter on the result set – so a performance killer.

1 comment:

  1. Where can I read about the completed implementation?

    I am interested to see the final Interface and to test the implementation if you have released it for general use.

    Was thinking about creating just this when I thought I would do a quick search and I stumbled across your blog.

    ReplyDelete