使用EF4 CTP5的存储库模式

Omu*_*Omu 5 entity-framework repository entity-framework-4

我正在尝试用ef4 ctp5实现存储库模式,我想出了一些东西,但我不是ef的专家,所以我想知道我做的是不是很好.

这是我的数据库上下文

public class Db : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和存储库:(简化)

public class Repo<T> : IRepo<T> where T : Entity, new()
{
        private readonly DbContext context;

        public Repo()
        {
            context = new Db();
        }

        public IEnumerable<T> GetAll()
        {
            return context.Set<T>().AsEnumerable();
        }

        public long Insert(T o)
        {
            context.Set<T>().Add(o);
            context.SaveChanges();
            return o.Id;
        }
}
Run Code Online (Sandbox Code Playgroud)

Kal*_*exx 10

您需要退后一步,考虑一下存储库应该做什么.存储库用于检索记录,添加记录和更新记录.您创建的存储库几乎不处理第一种情况,处理第二种情况但不能有效处理,并且根本不处理第三种情况.

大多数通用存储库都有一个接口

public interface IRepository<T> where T : class
{
    IQueryable<T> Get();
    void Add(T item);
    void Delete(T item);
    void CommitChanges();
}
Run Code Online (Sandbox Code Playgroud)

对于检索记录,您不能只调用整个集合,AsEnumerable()因为它会将该表的每个数据库记录加载到内存中.如果您只希望用户使用用户名username1,则不需要为数据库下载每个用户,因为这将是一个非常大的数据库性能命中,并且大客户端性能达不到任何好处.

相反,正如您将从我上面发布的界面中看到的那样,您想要返回一个IQueryable<T>对象. IQuerable允许调用存储库的任何类使用Linq并向数据库查询添加过滤器,并且一旦运行IQueryable,它就完全在数据库上运行,只检索所需的记录.数据库在对系统进行排序和过滤方面要好得多,因此最好尽可能多地在数据库上进行.

现在关于插入数据,你有正确的想法,但你不想SaveChanges()立即打电话.原因是Savechanges()在所有数据库操作排队后最好调用.例如,如果要在一个操作中创建用户及其配置文件,则无法通过方法,因为每次Insert调用都会导致数据插入到数据库中.

相反,你想要的是将Savechanges()调用分离到CommitChanges我上面的方法.

这也是处理数据库中更新数据所必需的.为了更改实体的数据,实体框架会跟踪它收到的所有记录,并监视它们以查看是否进行了任何更改.但是,您仍然必须告诉实体框架将所有已更改的数据发送到数据库.电话会发生这种情况context.SaveChanges().因此,您需要将其作为单独的调用,以便您能够实际更新当前实现无法处理的已编辑数据.


编辑: 您的评论让我意识到我看到的另一个问题.一个缺点是您正在存储库中创建数据上下文,这并不好.您真的应该让所有(或大多数)创建的存储库共享相同的数据上下文实例.

实体框架会跟踪实体所跟踪的上下文,如果您尝试在一个上下文中更新另一个实体,则会出现异常.当您开始编辑彼此相关的实体时,可能会出现这种情况.这也意味着您的SaveChanges()调用不是事务性的,并且每个实体都在其自己的事务中更新/添加/删除,这可能会变得混乱.

我在我的存储库中对此的解决方案是将其DbContext传递到构造函数中的存储库中.


Jos*_*rez 6

我可能会为此投票,但DbContext已经是一个存储库.当您将域模型公开为具体DbContext的集合属性时,EF CTP5会为您创建一个存储库.它提供了一个类似于接口的集合,用于访问域模型,同时允许您传递查询(作为linq或spec对象)以过滤结果.

如果您需要一个界面,CTP5不会为您提供一个界面.我已经将自己包装在DBContext中,并简单地从对象中公开了可公开获得的成员.它是可测试性和DI的适配器.

如果我说的话显然不明显,我会评论澄清.

  • 如果您要使用DDD,那么您需要创建一个公开聚合根的存储库.说一个客户存储库.将您的想象力用于客户拥有的所有关联以及对象图的深度.所有这些项都可以在您以DbSet属性的形式创建的EF上下文对象中获得.如果将上下文对象包装在作为客户存储库的对象内,那么您所做的只是传递给DBSet属性.如果你不打算使用DDD,你就不会关心聚合根,而且上下文本身就足够了. (2认同)