0

I am trying to implement UOW with repository pattern in my application.

While the independent repository is in place, but while multiple repositories in one transaction (UOW) is driving me crazy.

EF Relation One Customer - Many CustomerContacts

IUnitOfWork

 public interface IUnitOfWork
    : IDisposable
{
    void InitTransaction();

    void Rollback();

    void CommitTransaction();

}

BaseUOW

 public class UnitOfWork :
    IUnitOfWork
{

    protected DbContextTransaction _transaction;


    #region IUnitOfWork

     public void CommitTransaction()
    {
        _transaction.UnderlyingTransaction.Commit();
    }

    public void Rollback()
    {
        _transaction.UnderlyingTransaction.Rollback();
    }
    #endregion IUnitOfWork
}

CustomerUOW

 public class CustomerUOW
    : UnitOfWork
{
    private IRepository<CustomerRepository> _customerRepository;
    private IRepository<CustomerContactRepository> _customerContactRepository;

    public BranchUOW(IRepository<CustomerRepository> customerRepository, 
        IRepository<CustomerContactRepository> customerContactRepository)
    {
        _customerRepository= customerRepository;
        _customerContactRepository= customerContactRepository;
    }
    public override void InitTransaction()
    {
        _transaction.Commit();
    }


}

How do I implement my CustomerUOW so that Customer & CustomerContact repository share the same DbContext & goes in one transaction??

Note: Each repository has an implementation of CRUD in their separate class. like

 public class EntityRepository<C, T>
   : BaseRepository<FoodieTenantContext, T>
    where T : class
    where C : CustomerContext
{
    private DbSet<T> _dataSet
    {
        get
        {
            return _ctx.Set<T>();
        }
    }

    public EntityRepository(FoodieTenantContext ctx)
        : base(ctx)
    {
    }

    public override void Add(T entity)
    {
        _dataSet.Add(entity);
    }

    public override void Delete(T entity)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return _dataSet.Where(predicate).ToList<T>();
    }

    public override IEnumerable<T> GetAll()
    {
        return _dataSet.ToList<T>();
    }

    public override IQueryable<T> GetQuery()
    {
        return _dataSet;
    }

    public override int Save()
    {
        return _ctx.SaveChanges();
    }

    public override T Single(Expression<Func<T, bool>> predicate)
    {
        return _dataSet.Where(predicate).SingleOrDefault();
    }

    public override void Update(T entity)
    {
        _dataSet.Attach(entity);
        _ctx.Entry<T>(entity).State = EntityState.Modified;
    }
}

Thanks

Kgn-web
  • 7,047
  • 24
  • 95
  • 161
  • How are you creating your UoW? And how is `this.Database` initialised? – Michal Ciechan Jul 22 '18 at 09:58
  • @MichalCiechan I would be calling CustomerUOW from CustomerService which in turn would be called from api – Kgn-web Jul 22 '18 at 09:59
  • 3
    EF is already an implementation of UoW, why do you need another one? _"[First thing is, `DbContext itself implements the Unit of work pattern`](https://stackoverflow.com/a/26059863/585968)"_ –  Jul 22 '18 at 10:03
  • @MickyD, I don't want to tight couple my DAL with EF & unit test would be easy – Kgn-web Jul 22 '18 at 10:04
  • 1
    Over-engineering your app to include abstractions over an already present ORM which itself is an abstraction of any particular database vendor, is too costly in the short to medium term, and any long-term benefit is unlikely. To re-create a UoW offers little benefit to the underlying ORM –  Jul 22 '18 at 10:16
  • Also, you still haven't understood that DbContext is **already a unit of work** as indicated in the link. You seem to have _repository_ confused with UoW –  Jul 22 '18 at 10:19
  • "unit test would be easy" you can mock the dbContecxt!!! – Marcus Höglund Jul 22 '18 at 10:45

1 Answers1

1

On way would be to provide a Func<FoodieTenantContext, IRepository<CustomerContactRepository>> in your CustomerUow

public abstract class UnitOfWork : IUnitOfWork
{
    public UnitOfWork(FoodieTenantContext context)
    {
        this.Context = context;
    }

    // ... rest of the class
}

// usage could be like the following

public class CustomerUOW : UnitOfWork
{
    public CustomerService(Func<FoodieTenantContext, IRepository<CustomerRepository>> customerRepo
        , Func<FoodieTenantContext, IRepository<CustomerContactRepository>> contactRepo
        , FoodieTenantContext context) 
        : (context)
    {        
        _customerRepo = customerRepo(context);
        _contactRepo = contactRepo(context);
    }
}

Another option would be to create a RepositoryFactory, but this would mean you would have to expose a Context property from IRepository<T>

public class RepositoryFactory
{
    IServiceProvider _ioc; // This would be your IoC/DI Container

    public RepositoryFactory(IServiceProvider ioc)
    {
        _ioc = ioc;
    }

    // Resolve T passing in the provided `FoodieTenantContext` into the constructor
    public IRepository<T> CreateRepository<T>(FoodieTenantContext context) =>
        _ioc.Resolve<T>(context); 

}

Another solution could be (my least favourite) would be to expose methods in a RepositoryFactory for each type of IRepository<T>

public class RepositoryFactory
{
    public IRepository CreateCustomerContactRepository(FoodieTenantContext context) => 
        return new CustomerContactRepository(context);
}

Registering Func in Castle.Windsor

As per comment, to register Func<T> in Castle.Windsor you can try something like the following which is a modified version of Anton's answer to Func injecting with Windsor container question.. (I am not able to test this right now)

Container.Register(

  Component.For<Func<FoodieTenantContext, IRepository<CustomerRepository>>>()
           .Instance((FoodieTenantContext context) => Container.Resolve<IRepository<CustomerRepository>>(new {context = context}))
)

Otherwise you could try playing around with AsFactory(). For more info read Windsor documentation

And as a last resort, you can always fallback to manually creating a factory or switching IoC/DI containers that support Func<[TIn1, TIn2, ...], T> out of the box, or at least natively.

Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • Michael, if I do this way #1 suggested by you, then I am getting into some DI error ,how do I configure DI Setup (using CastleWindsor) – Kgn-web Jul 22 '18 at 10:58
  • Thanks Michael!!. Please upvote the post if you think it's worth – Kgn-web Jul 24 '18 at 09:35