4
\$\begingroup\$

In a project I'm working on, the data access layer has evolved to something that somewhat resembles the Unit Of Work and Repository patterns, at least they started with those names, I'm not entirely sure with how far they've drifted, if they can still be called that.

For classes data access, an implementation IWorkUnitFactory is passed as a constructor parameter from the IoC container. This is a rather simple interface:

public interface IWorkUnitFactory
{
    IWorkUnit Begin();
}

The IWorkUnit interface it returns looks like this:

public interface IWorkUnit : IDisposable
{
    T Insert<T>(T entity) where T : class;
    T Load<T>(int id) where T : class;
    void Delete<T>(int id) where T : class;
    void Delete<T>(T entity) where T : class;
    T Update<T>(T entity) where T : class;
    T InsertOrUpdate<T>(T entity) where T : class;
    IQueryable<T> Query<T>() where T : class;
    IEnumerable<T> Execute<T>(string queryName, IDictionary<string, object> parameters);

    void Commit();
    void Rollback();
}

My IWorkUnitFactory implementation for NHibernate basically just creates an NHibernate session, and passes it to the NHibernate implementation of IWorkUnit, which is just a rather thin wrapper around the NHibernate session, which starts a transaction in its constructor, and rolls it back in the Dispose implementation unless the Commit method is called.

Then, anywhere data access happens, it follows this pattern:

using(var workUnit = WorkUnitFactory.Begin())
{
    //Boring data access code here
    workUnit.Commit();
}

Next up are the Repository classes. These don't have any common inheritance or interface implementations, they just follow a few conventions, the main one being taking an IWorkUnit in its constructor. These classes contain the interaction with the IWorkUnit for operations like GetById, Find, Insert, Update, etc, and are responsible for small bits of logic (the Update method updates the LastUpdated field on the entity, Find determines what fields to search based on its parameters, etc).

So, for example, a Repository for the User entity might look something like this:

public class UserRepository
{
    private readonly IWorkUnit workUnit;
    public UserRepository(IWorkUnit workUnit)
    {
        this.workUnit = workUnit;
    }
    public virtual User GetByEmail(string email)
    {
        return workUnit.Query<User>().FirstOrDefault(u => u.Email == email);
    }
    public virtual User Update(User user, User updater)
    {
        user.ModifiedBy = updater;
        user.Modified = DateTimeOffset.Now;
        return workUnit.Update(user);
    }   
}

Then, code to update a user would look something like this:

using(var workUnit = WorkUnitFactory.Begin())
{
    var userRepo = new UserRepository(workUnit);
    var userToEdit = userRepo.GetByEmail("test@example.com");

    //Do some stuff to userToEdit here

    userRepo.Update(userToEdit, currentRequestUser); //currentRequestUser is just whoever is performing the action
    workUnit.Commit();
}

Any feedback on better naming for these things, or better methods? This has served me rather well, with the only thing I don't really like is creating the repository instances like that, But I can't think of a way to handle transactions and keep the somewhat lower level data access code away from higher level business logic.

\$\endgroup\$
2
\$\begingroup\$

If all your business classes (e.g., User) could inherit from a common class or interface that has Modified and ModifiedBy properties, you could make your Repository a generic Repository<T>. Class-specific methods like GetByEmail would be replaced with generalized methods that take Expressions. Would that address your concern about "creating the repository instances like that", while keeping the business-level concerns (e.g., the fact that you're retrieving by email and not something else) away from your data access code?

Here's an untested and even uncompiled example of what I'm talking about.

public interface IBusinessEntity
{
    User ModifiedBy { get; set; }
    DateTime Modified { get; set; }
}

public class Repository<T> where T : IBusinessEntity
{
    private readonly IWorkUnit workUnit;
    public Repository(IWorkUnit workUnit)
    {
        this.workUnit = workUnit;
    }
    public virtual T Get(Expression<Func<T,bool>> expression)
    {
        return workUnit.Query<T>().FirstOrDefault(expression);
    }
    public virtual T Update(T objToUpdate, User updater)
    {
        objToUpdater.ModifiedBy = updater;
        objToUpdate.Modified = DateTimeOffset.Now;
        return workUnit.Update(objToUpdate);
    }   
}

As for your naming conventions, they make sense to me.

Just one more idea: Unless your application will always be used in just one timezone, and hosted in the same timezone, you might consider storing your Modified property as UTC.

\$\endgroup\$
  • \$\begingroup\$ Will it work in SOA environment, for example like an Enteprise Domain Repository service? \$\endgroup\$ – onof Sep 7 '12 at 7:41
  • \$\begingroup\$ It's very hard to mock LINQ Expressions. If you are going to unit test your business layer, you may want to avoid them. \$\endgroup\$ – Akira Yamamoto Oct 14 '14 at 18:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.