【问题标题】:Linq to Entities - 3-tier ArchitectureLinq to Entities - 3 层架构
【发布时间】: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”??)

【问题讨论】:

    标签: c# linq-to-entities dao 3-tier


    【解决方案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】:

    我认为两者都有效。主要的是你应该让你的对象上下文相当短暂(恕我直言)。因此我认为你有两个选择:-

    1. 在单个方法调用中创建/销毁上下文,例如根据您的第二个示例使用 using 语句。

    2. 在您创建/销毁存储库时创建/销毁上下文 - 在这种情况下,您的存储库应该实现 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&lt;T&gt;

    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 方法,然后处置
    猜你喜欢
    • 2010-10-24
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多