HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Unit Of Work/Repository usage

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
usagerepositoryworkunit

Problem

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 entity) where T : class;
    T Load(int id) where T : class;
    void Delete(int id) where T : class;
    void Delete(T entity) where T : class;
    T Update(T entity) where T : class;
    T InsertOrUpdate(T entity) where T : class;
    IQueryable Query() where T : class;
    IEnumerable Execute(string queryName, IDictionary 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 determi

Solution

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. 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 where T : IBusinessEntity
{
    private readonly IWorkUnit workUnit;
    public Repository(IWorkUnit workUnit)
    {
        this.workUnit = workUnit;
    }
    public virtual T Get(Expression> expression)
    {
        return workUnit.Query().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.

Code Snippets

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);
    }   
}

Context

StackExchange Code Review Q#15149, answer score: 2

Revisions (0)

No revisions yet.