Entity framework 使用EF4 CTP5的存储库模式

Entity framework 使用EF4 CTP5的存储库模式,entity-framework,entity-framework-4,repository,Entity Framework,Entity Framework 4,Repository,我试图用ef4 ctp5实现存储库模式,我提出了一些想法,但我不是ef方面的专家,所以我想知道我所做的是否好。 这是我的数据库上下文 public class Db : DbContext { public DbSet<User> Users { get; set; } public DbSet<Role> Roles { get; set; } } 公共类Db:DbContext { 公共数据库集用户{get;set;} 公共数据库集角色{get;se

我试图用ef4 ctp5实现存储库模式,我提出了一些想法,但我不是ef方面的专家,所以我想知道我所做的是否好。

这是我的数据库上下文

public class Db : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Role> Roles { get; set; }
}
公共类Db:DbContext
{
公共数据库集用户{get;set;}
公共数据库集角色{get;set;}
}
和存储库:(简化)

公共类Repo:IRepo其中T:Entity,new()
{
私有只读DbContext上下文;
公开回购()
{
context=newdb();
}
公共IEnumerable GetAll()
{
返回context.Set().AsEnumerable();
}
公共长插页(TO)
{
context.Set().Add(o);
SaveChanges();
返回o.Id;
}
}

您需要后退一步,思考存储库应该做什么。存储库用于检索记录、添加记录和更新记录。您创建的存储库只处理第一种情况,处理第二种情况但效率不高,根本不处理第三种情况

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

public interface IRepository<T> where T : class
{
    IQueryable<T> Get();
    void Add(T item);
    void Delete(T item);
    void CommitChanges();
}
公共接口i假设,其中T:class
{
IQueryable Get();
无效添加(T项);
作废删除(T项);
无效提交更改();
}
对于检索记录,不能只使用
AsEnumerable()
调用整个集合,因为这样会将该表的每个数据库记录加载到内存中。如果您只需要用户名为
username1
的用户,则不需要下载数据库的每个用户,因为这将对数据库性能造成很大的影响,并且对客户端性能造成很大的影响,但毫无益处

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

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

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

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


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

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


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

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

如果您需要接口,CTP5不会为您提供接口。我已经围绕DBContext包装了我自己的内容,并简单地公开了对象中公开的可用成员。它是可测试性和DI的适配器


如果我所说的不明显,我将发表评论以澄清。

@KallDrexx我展示了一段非常简化的代码,我只想知道主结构是否正确,比如是否可以将新的Db()放入回购协议的构造函数中,并调用context.Set.Something()?更新了第一部分的答案。调用
context.Set.XXX
是正确的,这也是我在存储库中的做法。@KallDrexx我做一个web应用程序,如果每个人都使用相同的上下文,有人会调用SaveChanges(),这会非常混乱,不是吗?@Omu:每个请求应该有一个上下文。大多数DI库都支持这种行为。您可以通过IoC创建存储库,但仍然需要让存储库将DbContext作为构造函数参数,然后通过IoC实例化它。这将允许跨web请求共享DbContext如果要使用DDD,则需要创建一个公开聚合根的存储库。比如说客户存储库。利用您的想象力了解客户的所有关联以及对象图的深度。所有这些项目都可以在表单中创建的EF上下文对象上使用
public interface IRepository<T> where T : class
{
    IQueryable<T> Get();
    void Add(T item);
    void Delete(T item);
    void CommitChanges();
}