3
\$\begingroup\$

I am implementing the unit of work pattern to be used in the logic layer of my application. The goal is to abstract away the tasks of managing transactions / connections.

My implementation relies on generic repositories that exists during the life time of the unit of work class.

UnitOfWork

public class UnitOfWork : IUnitOfWork
{
    private MySqlConnection connection;
    private MySqlTransaction transaction;

    private List<object> repos = new List<object>();

    public UnitOfWork(string connectionStringId)
    {
        string connectionString = ConfigurationManager.ConnectionStrings[connectionStringId].ConnectionString;
        connection = new MySqlConnection(connectionString);
        connection.Open();
        transaction = connection.BeginTransaction();
    }

    public IUnitOfWork Add<T>() where T : class {
        IRepository<T> repo = new Repository<T>(connection, transaction);
        repos.Add(repo);   
        return this;
    }

    public IRepository<T> Get<T>() where T : class
    {
        foreach (object repo in repos)
        {
            if (repo is IRepository<T>)
            {
                return (IRepository<T>) repo;
            }
        }
        return null;
    }

    public void Dispose() {
        try
        {
            transaction.Commit();
        }
        catch (InvalidOperationException e)
        {
            //Thrown if the transaction has already been rolled back.
        }
        catch (MySqlException e)
        {
            transaction.Rollback();
            throw e;
        }
        transaction.Dispose();
        connection.Close();
        connection.Dispose();
    }
}

Repository

 public class Repository<T> : IRepository<T> where T : class {

    private MySqlConnection connection;
    private MySqlTransaction transaction;

    public Repository(MySqlConnection connection, MySqlTransaction transaction) {      
        this.connection = connection;
        this.transaction = transaction;
    }

    public long Create(T item) {
        try {
            return connection.Insert(item);
        } catch (MySqlException e) {
            transaction.Rollback();
            throw e;
        }
    }

    public bool Delete(T item) {
        try {
            return connection.Delete(item);
        } catch (MySqlException e) {
            transaction.Rollback();
            throw e;
        }     
    }

    public bool Update(T item) {
        try {
            return connection.Delete(item);
        } catch (MySqlException e) {
            transaction.Rollback();
            throw e;
        }  
    }

    public T Get(long id) {           
        return connection.Get<T>(id);         
    }

    public IEnumerable<T> List() {
        return connection.GetAll<T>();           
    }
}

Example Usage

using (IUnitOfWork UOW = new UnitOfWork("the_db")
    .Add<A>()
    .Add<B>())
{
    IRepository<A> aRepo = UOW.Get<A>();
    IRepository<B> bRepo = UOW.Get<B>();

    aRepo.Create(new A( ... ) );
    aRepo.Create(new A( ... ));

    int aCount = aRepo.List().Count();
    Console.WriteLine(aCount); // 2

    long id = bRepo.Create(new B( ... ));
    B b = bRepo.Get(id);
    bRepo.Delete(b);
}

Test

#Test to see if the transaction is rolled back on unsuccessful create.
using (IUnitOfWork UOW = new UnitOfWork("the_db")
    .Add<User>()
    .Add<Schedule>())
{
    UOW.Get<User>().Create(new User
    {
        Username = "test",
        Email = "hello",
        Hash = "lol"
    });
    try
    {
        UOW.Get<Schedule>().Create(new Schedule
        {
            Name = "Invalid",
            UserId = 123
        });
        //Fails because UserId is a foreign key that references non existing user.
    }
    catch (MySqlException e)
    {           
        Assert.AreEqual(0, UOW.Get<User>().List().Count());
        return;
    }
    Assert.Fail();
} 

I am thinking about how to enforce that the list of objects in UnitOfWork are in fact repositories but haven't figured it out how yet.

\$\endgroup\$
  • \$\begingroup\$ UnitOfWork usually implemented as the DbContex facade, having the of IDisposable pattern put inside of 'using' keyword scope gives the transactional consistency, logging, etc. \$\endgroup\$ – valerysntx Nov 16 '18 at 4:13
  • \$\begingroup\$ @valery.sntx What do you mean with implemented as the DbContext facade? \$\endgroup\$ – Tagor Nov 16 '18 at 13:03
  • \$\begingroup\$ gist.github.com/danielgreen/5706441#file-unitofwork-cs \$\endgroup\$ – valerysntx Nov 17 '18 at 21:42

Your Answer

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

Browse other questions tagged or ask your own question.