geek# techno-babble for the masses

Implementing ISession in EF4

In a previous post regarding the ISession from Rob Conery, a commenter asked about our implementation of ISession with regards to EF4. Rather than send him the specific code, I figured I’d make another blog post to outline exactly how we use it here at work.

Basically, each of our projects has a "Data" layer where we store everything needed to communicate with the database. This is also where the ISession interface lives, so it’s easy to provide the implementation. There is one caveat, however, because we need to provide a generic EF4 implementation that can work with any data context. In our projects, sometimes we need to speak with multiple databases, and then it becomes just a matter of plugging the right entity collection into our implementation. Here’s what the code looks like:

public class EntityFrameworkSession<O> : ISession where O : ObjectContext, new() {  
    protected readonly ObjectContext _db;

    public EntityFrameworkSession() {
        _db = new O();
    }

    public virtual void CommitChanges() {
        _db.SaveChanges();
    }

    public virtual void Refresh<T>(T item) where T : class {
        _db.Refresh(RefreshMode.StoreWins, item);
    }

    public virtual void Delete<T>(Expression<Func<T, bool>> expression) where T : class {
        IQueryable<T> query = _db.CreateObjectSet<T>().Where(expression);

        foreach (T item in query) {
            _db.DeleteObject(item);
        }
    }

    public virtual void Delete<T>(T item) where T : class {
        if (item == null)
            throw new ArgumentNullException("item");

        _db.DeleteObject(item);
    }

    public virtual void DeleteAll<T>() where T : class {
        ObjectSet<T> query = _db.CreateObjectSet<T>();

        foreach (T item in query) {
            _db.DeleteObject(item);
        }
    }

    public virtual T Single<T>(Expression<Func<T, bool>> expression) where T : class {
        return All<T>().SingleOrDefault(expression);
    }

    public virtual IQueryable<T> All<T>() where T : class {
        return _db.CreateObjectSet<T>().AsQueryable();
    }

    public virtual void Add<T>(T item) where T : class {
        _db.CreateObjectSet<T>().AddObject(item);
    }

    public virtual void Add<T>(IEnumerable<T> items) where T : class {
        foreach (T item in items) {
            _db.CreateObjectSet<T>().AddObject(item);
        }
    }

    public virtual void Update<T>(T item) where T : class {
        // EF4 tracks updates on its own, so nothing is needed here
        return;
    }

    public virtual void Dispose() {
        _db.Dispose();
    }
}

As you can see, it’s actually pretty small. The beauty comes from the fact that it’s completely generic. When we create an EF4 Entity Data Model (*.EDMX file), we name the data context, and then we can pass it into this implementation, like so:

// First, we need to build the session
var session = new EntityFrameworkSession<NorthwindEntities>();

// Now I can get any table I want from the session.
var products = session.All<Product>();

// Maybe I want and individual item.
var product = session.Single<Product>(x => x.ProductID = 1);

// We can use the more verbose LINQ syntax, too.
var products = from p in session.All<Product>()  
               where p.Price > 2.50m
               select p;

As you can see, working with ISession is pretty easy from this implementation. One thing I haven’t covered in this post is support for logical deletes. We have another subclass for that which uses this EF4 implementation as its base. This is why all the methods in our implementation are virtual. I’ll save that implementation for another post!