Linq to Entities - 3 层架构
Posted
技术标签:
【中文标题】Linq to Entities - 3 层架构【英文标题】:Linq to Entities - 3-tier Architecture 【发布时间】:2011-10-02 17:25:12 【问题描述】:在过去的几个月里,我学到了很多关于 Linq-To-Entities 和带有 DAO/DAL/Repository 的 3 层架构的知识。现在我脑子里有一些事情一直困扰着我。我有这三个问题,您将在下面看到。
有很多方法可以使存储库工作,但是使存储库以性能方式工作的“方法”是什么。
1) 在构造函数中初始化一个数据上下文
public class Repository : IRepository
private Datacontext context;
public Repository()
context = new Datacontext();
public IList<Entity> GetEntities()
return (from e in context.Entity
select e).ToList();
2) 使用“使用”
public class Repository : IRepository
public IList<Entity> GetEntities()
using (Datacontext context = new Datacontext())
return (from e in context.Entity
select e).ToList();
3) 另一种方式(请发表评论)
我会把你的建议放在这里供其他人评论
另外,似乎有些人说存储库应该返回一个 IQueryable 给业务层,而另一些人说最好返回一个 IList。您对此有何看法?
上面第一个问题中的代码示例都是指向Repository,但是在businesslayer中实现repository的最佳方式是什么(在构造函数中初始化,使用“Using”??)
【问题讨论】:
【参考方案1】:如果您不想链接您的方法以进行最准确的查询,请使用第一种情况。例如:
public class Repository : IRepository
private Datacontext context;
public Repository()
context = new Datacontext();
public IQueryabale<Entity> SelectAllEntities()
return context.Entity.Where(e=>! e.IsObsolote);
public IQueryable<Entity> SelectAllEntityRelatedToAnotherEntity(Entity otherEntity)
return this.SelectAllEntities().Where(e=>e.RelatedEntityId == otherEntity.Id);
编辑
You can use it in collaboration with your business layer like this:
public class EntityManager()
public IQueryable<Entities> FindAllApprovedEntities(Entity other)
return new Repository().SelectAllEntityRelatedToAnotherEntity(other).Where(e=>e.Approved);
【讨论】:
这看起来很公平,但在这里你也遇到了在完成工作后处理上下文的问题。另外,最后一个方法“SelectAllEntityRelatedToAnotherEntity”不是业务层的工作吗? @Julian 业务层的工作是将条件添加到 Iqueryable。例如查看我的编辑【参考方案2】:我认为两者都有效。主要的是你应该让你的对象上下文相当短暂(恕我直言)。因此我认为你有两个选择:-
在单个方法调用中创建/销毁上下文,例如根据您的第二个示例使用 using 语句。
在您创建/销毁存储库时创建/销毁上下文 - 在这种情况下,您的存储库应该实现 IDisposable 并且本身被包装在 using 语句中,并且应该是短暂的。这种方法的好处是您的存储库方法只需执行查询,没有使用 (new ObjectContext()) 污染方法;另一方面是 reposibility 传递给客户端来处理存储库。使用这种机制意味着您还可以在 IQueryable 中编写查询(前提是您在处理存储库之前执行查询)。例如:
公共类存储库:IDisposable DataHubContext 上下文 = new DataHubContext();
public IQueryable<Payment> GetPayments()
return context.Payments;
public void Dispose()
context.Dispose();
在 SO 中格式化有点滑稽 - 抱歉....然后在您的调用代码中:-
public class ClientCode
public void DisplayPaymentsOnScreen()
Payment[] payments;
using (var repository = new Repository())
payments = repository.GetPayments().Where(p => p.Amount > 100).ToArray();
// Do stuff with the data here...
【讨论】:
那么在 webclient 离开站点后处理存储库的普通垃圾收集器怎么样? (过了一会儿) 如果您将存储库放在 using 语句中,那么一旦它离开 using 语句的范围,它将被标记为 GC。这与 Web 客户端离开您的站点无关——更多的是与存储库的范围有关。只需确保在存储库的处置方法中处置 ObjectContext :-) 是的,当您使用 using 语句时,它会。但是这里的大多数人都说我不应该使用 using 语句,而是在存储库的构造函数中创建上下文。您对此有何看法? 我上面的第二个建议是确实在构造函数中创建对象上下文。使存储库类本身是一次性的,并在您的客户端类中将其包装为 using 语句。然后,当您处置存储库时,上下文也会被处置。我将使用代码示例更新我的答案。 嗯,这看起来很不错,Isaac,性能是否会受到影响,因为每次您向业务层询问某些内容时,它都会创建并随后处理一个新的存储库?【参考方案3】:对我自己来说,我总是会返回IQueryable<T>
public IQueryable<Entity> GetEntities()
return from e in context.Entity select e;
您必须阅读有关延迟执行的信息 http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx 我使用它以便在业务逻辑或 UI 中查询实体的确切部分而不是整个实体(就像 select *
)
我更喜欢在构造函数中初始化上下文
public class Repository : IRepository
private Datacontext context;
public Repository()
context = new Datacontext();
public IQueryable<Entity> GetEntities()
return from e in context.Entity select e;
public int Save()
// Return the number of the affected rows to determine in your code whether your query executed or not
return context.SubmitChanges();
注意:另外,在您设计 EF 存储库时,请确保您在所有存储库中都有一个上下文实例,以避免在更新和删除过程中出错。 p>
我有一个我在公司创建的通用存储库,我计划很快将它写在博客上,它允许您轻松地进行 CRUD 操作,并且您可以使用很少的代码行来扩展它。完成后,我将使用 URL 更新答案
【讨论】:
教授的陈述很好。我稍后会查看您的链接,但是当用户(webclient)存在时,您如何处理您的存储库?除了等待垃圾收集器之外,我看到 Isaac 在他的帖子中提到了 IDisposable。你用那个还是? 我把它留在这里供 GC,因为我给你的代码只是一个例子,但在我的真实代码中,因为我提到了所有存储库的一个上下文,所以我不能从存储库中处理它,我有一个上下文(我的上下文)对于系统中的所有存储库,在这里我可以考虑处置上下文,但我更喜欢将其保留给 GC。我曾经看到这必须在 Linq to SQL 中完成,但 EF 没有必要 所以你说我们应该让 GC 完成他的工作。好的,存储库和业务层的方法呢。存储库是否应该只有 save/update、delete、find(id)、findall 方法。还是可以在存储库中使用类似“findAllReadBooks()”之类的方法? 会有一个使用泛型的继承,并且基础存储库具有主要的 CRUD,然后在实体存储库类中,您可以根据您的业务逻辑添加更多内容。关于 GC 我不能 100% 确定我们应该把它留给 GC,而不是自己处理,我必须对此进行调查。【参考方案4】:我认为这取决于您的需求...
如果您的存储库仅用于获取数据,那么我可能会使用 Using 技术,但老实说,您何时需要一个仅用于获取数据的存储库。您还需要添加和更新,所以我肯定会选择全局数据上下文。这样做的好处是您可以更新实体模型,然后将更改保存回来。如果每次调用都使用不同的数据上下文,则无法持久保存更改。
示例存储库类似于...
public class Repository : IRepository
private Datacontext context;
public Repository()
context = new Datacontext();
public IList<Entity> GetEntities()
return (from e in context.Entity
select e).ToList();
public void Save()
context.SubmitChanges();
...然后您可以进行许多数据更改并一次性提交到数据库。要考虑的另一点是,在您的 GetEntities 中,您调用了 ToList()。当你调用它时,你实际上是在那里执行数据库查询。如果您打算对结果进行进一步的逻辑处理,您可能希望返回一个 IQueryable,并且仅在您确实需要使用列表时才调用 ToList
【讨论】:
这是我对存储库的第一个想法。返回列表并在构造函数中初始化数据上下文。在 webclient 离开网站后,你将如何处理存储库? 我没有去过,AFAIK 你不必那样做。我认为它超出范围后会清除,C#处理这些事情。我已经阅读了其他一些内容,我现在肯定会为自己仔细检查一下……你提供一些知识,你获得一些知识,然后循环重复:D Jup,很好,我也会再研究一下。每当我知道处理这类事情的最佳方法时,我都会编辑我的帖子。感谢您的帮助!【参考方案5】:我更喜欢 using 模式,因为它可以让代码更简洁,因为我们知道 db 上下文的范围,并且可以清楚地说明何时处理它,这在构造函数的情况下很难说。
另外,我认为您不能在“使用”的情况下返回 IQueryable,因为一旦 using 块退出,数据库上下文将被释放,然后您无法在业务层中使用返回的 IQueryable。
【讨论】:
但是,每当我的客户端/GUI 层进行编辑时,这不是问题吗?就像向 BookReader-entity 添加 Book-entity 一样? (因为上下文已被释放,所以每当我调用 Bookreader.Books.Add(...) 它不会更新我的 Bookreader 实体) 添加可以简单地通过存储库完成,方法是将新的 Book 对象传递给 add 方法,此方法创建一个上下文,添加书籍,调用已保存并释放上下文。在编辑的情况下,您将打开上下文,通过书籍 id 找到书籍,对书籍进行属性更改,然后在上下文中调用 save 方法,然后处置以上是关于Linq to Entities - 3 层架构的主要内容,如果未能解决你的问题,请参考以下文章
这是如何运作的? LINQ to Entities 触发程序集的加载?
LINQ to Entities does not recognize the method , and this method cannot be translated into a store e
使用 Linq to Entities 在一次操作中获取 COUNT 和 SKIP TAKE