4
\$\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\$
3
  • \$\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, 2018 at 4:13
  • \$\begingroup\$ @valery.sntx What do you mean with implemented as the DbContext facade? \$\endgroup\$
    – Tagor
    Nov 16, 2018 at 13:03
  • \$\begingroup\$ gist.github.com/danielgreen/5706441#file-unitofwork-cs \$\endgroup\$
    – valerysntx
    Nov 17, 2018 at 21:42

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.