简体   繁体   中英

how to implement UOW in C# with EF6

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM