使用UnitOfWork和存储库模式与实体框架

时间:2013-05-09 17:26:53

标签: c# entity-framework repository-pattern unit-of-work

我将在我的数据访问层中使用存储库和UnitOfwork来执行此操作,查看一个联系人aggregateroot

 public interface IAggregateRoot
    {
    }

这是我的Generic存储库界面:

 public interface IRepository<T>
        {
            IEnumerable<T> GetAll();
            T FindBy(params Object[] keyValues);
            void Add(T entity);
            void Update(T entity);
            void Delete(T entity);

        }

和模型中的POCO Contact类

 public class Contact :IAggregateRoot
        {
            public Guid Id { get; set; }
            public string Name { get; set; }
            public string Email { get; set; }
            public string Title { get; set; }
            public string Body { get; set; }
            public DateTime CreationTime { get; set; }
        }

这是我继承自IRepository的IContactRepository,也可能是自己的方法

 public interface IContactRepository : IRepository<Contact>
        {
        }

现在我已经完成了IUitOfWork和UnitOfwork这样的

public interface IUnitOfWork 
    {
        IRepository<Contact> ContactRepository { get; }
    }

public class UnitOfWork : IUnitOfWork
    {
        private readonly StatosContext _statosContext = new StatosContext();
        private IRepository<Contact> _contactUsRepository;

 public IRepository<Contact> ContactRepository
        {
            get { return _contactUsRepository ?? (_contactUsRepository = new Repository<Contact>(_statosContext)); }
        }
}

关于我的存储库

public class Repository<T> : IRepository<T> where T : class, IAggregateRoot
    {
       //implementing methods 
    }

我可以通过在服务中使用UnitOfwork访问存储库来执行所有CRUD操作,例如:

_unitOfWork.ContactRepository.Add(contact);
            _unitOfWork.SaveChanges();

但我想这样做

_

ContactRepository.Add(contact);
            _unitOfWork.SaveChanges();

(通过_unitOfWork.ContactRepository通过_ContactRepository No获取CRUD和泛型方法) 因为我想将ContactRepository方法提供给一些特定的查询, 有人帮忙吗?

3 个答案:

答案 0 :(得分:11)

这不是直接对您的问题的答案,但它可能会简化一些事情并减少重复。

当您使用例如EntityFramework Power Tools对Code First进行逆向工程(或者一般只使用Code First),最终得到一个DbContext类作为UoW和存储库,例如:

public partial class YourDbContext : DbContext
{
    public DbSet<Contact> Contacts {get; set;}
}

现在,如果你想让事情变得可测试,那就有一个简单的方法:引入一个非常薄的界面:

public interface IDbContext
{
    IDbSet<T> EntitySet<T>() where T : class;
    int SaveChanges();
    //you can reveal more methods from the original DbContext, like `GetValidationErrors` method or whatever you really need.
}

然后使用部分类的第二部分创建另一个文件:

public partial class YourDbContext : IDbContext
{
     public IDbSet<T> EntitySet<T>() where T : class
     {
         return Set<T>();
     }
}

钽哒!现在,您可以IDbContext注入YourDbContext进行备份:

//context is an injected IDbContext:
var contact = context.EntitySet<Contact>().Single(x => x.Id == 2);    
contact.Name = "Updated name";
context.EntitySet<Contact>().Add(new Contact { Name = "Brand new" });
context.SaveChanges();

现在,如果您想控制上下文的处理,那么您必须编写自己的( gasp IDbContextFactory(通用或不通用,具体取决于您的需要)并注入该工厂。

现在不需要编写自己的FindAddUpdate方法,DbContext会适当地处理,更容易引入显式事务,一切都很好地隐藏接口后面(IDbContextIDbSet)。

顺便说一下,IDbContextFactory将等同于NHibernate的ISessionFactoryIDbContext - ISession。我希望EF也开箱即用。

答案 1 :(得分:2)

我同意Doctor,DbContext已经是一个UnitOfWork,并且在它之上添加另一个UoW抽象通常是多余的,除非你认为你很可能在将来切换数据库技术。

然而,我不同意将DbSet视为存储库,因为这会将您的查询与使用它们的方法紧密结合。如果您需要更改查询,则必须在使用它的任何地方执行此操作。

我更喜欢使用独立的存储库(或服务接口,它们提供类似的功能),或者使用更多的CQRS系统进行命令/查询分离,这是一个使用查询对象。

答案 2 :(得分:0)

在UnitOfWork类中,您需要实现DBContext或ObjectContext。

无论系统如何,UnitOfWork都会隔离所有交易。 EF仅用于数据库连接。即使你的系统只使用数据库,最好还是保留一个单独的UnitOfWork类,以便将来扩展。

在工作单元Commit()中,您可以调用内部实现的DBContext.SaveChanges()。

在unitofwork内声明的所有存储库都可以访问此DBcontext。所以在DBcontext中添加或删除存储库,unitOfwork会提交它。

当您拥有跨越不同存储的场景时,例如:Cloud Blob,表存储等。您可以在UnitofWork中实现它们,就像您实现EF上下文一样。一些存储库可以访问表存储和一些EF上下文。

提示:实现ObjectContext而不是DBContext为缓存方案提供了优势。您可以在扩展框架方面有更多选择。