【问题标题】:ASP.NET MVC using the Repository Pattern使用存储库模式的 ASP.NET MVC
【发布时间】:2011-04-19 06:25:53
【问题描述】:

目前我正在使用 EF 并在我的所有操作中直接使用它的数据上下文,但是自从我开始阅读有关松散耦合和可测试性的内容后,我认为这不是最好的方法。在我开始重构所有当前代码之前,我试图了解所有优点和缺点。

问题 1: 考虑到每个实体都需要自己的存储库,因此必须设置自己与数据源的连接(假设使用 EF 的数据库),如果我需要来自单个页面上 5 个不同实体的数据,这不会带来很多开销吗?

问题 2: 在我在网上找到的所有示例中,我也看到大多数人(甚至像 shanselman 这样的人)使用由 LINQ 或 EF 生成的实体类来实现存储库模式,这是否违背了存储库模式的目的关于松耦合?另一方面,还有什么替代方法,将 POCO 类与 AutoMapper 结合使用? (这让我有点害怕)

我希望一些人可以对此有所了解,因为目前我有点困惑存储库模式是否是网站的正确选择。

【问题讨论】:

    标签: asp.net asp.net-mvc linq-to-sql entity-framework repository-pattern


    【解决方案1】:

    您可以阅读this book。有一个使用 Repository 模式和 LINQ 的好例子。
    还有这篇文章Using Repository and Unit of Work patterns with Entity Framework 4.0。

    【讨论】:

    • 前段时间看过那本书,打算再看看他的例子
    • 第二个链接最能引导我了解我最终实现的内容。
    【解决方案2】:

    ObjectContext 使用连接池,因此它不会像您想象的那样低效。此外,SQL 服务器(即 MSSQL)确实针对大量并发连接进行了优化。

    至于如何实现它,我会使用一些 IRepository 接口。然后,您可以创建特定的接口,即 PostRepository > IRepository,最后在具体的类中实现它(例如,一个真实的类和一个用于测试的假内存类)。

    【讨论】:

    • 我知道 sql 服务器使用连接池这一事实,但我主要关心的是 EF 或 LINQ 维护其上下文的方式,似乎在水下发生了很多事情,而且时间为 5(或更多) ) 等于很多开销,还是我完全是这里的基础?
    • 它的设计正是为了避免这种开销。您通常可以一次创建大量 ObjectContext 对象而不会出现任何问题。但不要走错路;你不应该粗心。
    • EF/LINQ 在后台使用 ADO.NET,因此它们最终使用相同的连接池机制。
    【解决方案3】:

    首先,我不知道每个实体都需要拥有自己的存储库,所以我会放弃这个限制。

    对于 Scott H 的实现,我假设您指的是 Nerd Dinner 应用程序,他自己承认这并不是真正的存储库模式。

    如您所料,存储库模式的目标是将数据存储与其上层隔离开来。这不仅仅是出于测试原因,它还允许您在不影响 UI/业务逻辑的情况下更改后备存储。

    在纯粹的术语中,您将创建 POCO,您将从存储库返回到您的 BL,通过使用接口定义存储库合约,您可以传递并使用该接口而不是具体实现。这将允许您传入任何实现 Repository 接口的对象,无论是您的实时存储库还是模拟存储库。

    实际上,我使用带有 MVC 的存储库和 Linq to SQL 作为我的后备存储,这使我在实际后备存储上具有一定程度的灵活性,因此我在我的 BL 中使用手工制作的 L2S 对象,这些具有额外的字段和功能'不坚持到后备商店。通过这种方式,我从 L2S 方面获得了一些很棒的功能、更改跟踪、对象层次结构等,同时还允许我用模拟存储库代替 TDD。

    【讨论】:

    • 我认为我正在成为那些纯粹主义者之一,如果我想防止大量左手右手编码,自动映射器是唯一的实例化方法吗?手工制作的 L2S 对象让我很感兴趣,就像 POCO 但不是真的?
    • 您可以使用自动映射器,然后扩展类(我相信像 L2S 这样的 EF 创建部分)但是 EF 带有很多其他问题,我还不需要使用它至少知道一个人想要倾倒它。手工制作的 L2S 对象实际上是 POCO,但使用 Linq To SQL 属性进行修饰,并通过存储库实例化的数据上下文进行访问。
    【解决方案4】:

    您在确定将实体用作业务对象的困难方面一针见血。经过多次尝试和错误,这是我们已经适应的模式,对我们来说效果很好:

    我们的应用分为模块,每个模块又分为三层:Web(前端)、Core(业务)和Data。在我们的例子中,这些层中的每一层都有自己的项目,因此有一个强制执行来防止我们的依赖项变得紧密耦合。

    Core 层包含实用程序类、POCO 和存储库接口。

    Web 层利用这些类和接口来获取所需的信息。例如,MVC 控制器可以将特定的存储库接口作为构造函数参数,因此我们的 IoC 框架会在创建控制器时注入该存储库的正确实现。存储库接口定义了返回我们的 POCO 对象的选择器方法(也在核心业务层中定义)。

    数据 层的全部职责是实现核心层中定义的存储库接口。它有一个实体框架上下文来表示我们的数据存储,但它不是返回实体(技术上是“数据”对象),而是返回核心层中定义的 POCO(我们的“业务”对象)。

    为了减少重复,我们有一个抽象的通用 EntityMapper 类,它提供了将实体映射到 POCO 的基本功能。这使得我们的大多数存储库实现都非常简单。例如:

    public class EditLayoutChannelEntMapper : EntityMapper<Entity.LayoutChannel, EditLayoutChannel>,
        IEditLayoutChannelRepository
    {
        protected override System.Linq.Expressions.Expression<Func<Entity.LayoutChannel, EditLayoutChannel>> Selector
        {
            get
            {
                return lc => new EditLayoutChannel
                                 {
                                     LayoutChannelId = lc.LayoutChannelId,
                                     LayoutDisplayColumnId = lc.LayoutDisplayColId,
                                     ChannelKey = lc.PortalChannelKey,
                                     SortOrder = lc.Priority
                                 };
            }
        }
        public EditLayoutChannel GetById(int layoutChannelId)
        {
            return SelectSingle(c => c.LayoutChannelId == layoutChannelId);
        }
    }
    

    感谢EntityMapper基类实现的方法,上面的仓库实现了如下接口:

    public interface IEditLayoutChannelRepository
    {
        EditLayoutChannel GetById(int layoutChannelId);
        void Update(EditLayoutChannel editLayoutChannel);
        int Insert(EditLayoutChannel editLayoutChannel);
        void Delete(EditLayoutChannel layoutChannel);
    }
    

    EntityMapper 在它们的构造函数中做的很少,所以如果一个控制器有多个存储库依赖项是可以的。不仅实体框架重用连接,而且实体上下文本身仅在调用存储库方法之一时创建。

    每个模块还有一个特殊的Test项目,其中包含对这三层中的类的单元测试。我们甚至想出了一种方法来使我们的存储库和其他数据访问类在某种程度上是可单元测试的。现在我们已经设置了这个基本的基础架构,向我们的 Web 应用程序添加功能通常非常顺利并且不太容易出错。

    【讨论】:

      【解决方案5】:

      问题 2:避免这种情况的一种方法是使用“ADO.NET C# POCO Entity Generator”之类的东西。

      【讨论】:

      • 我现在使用 ADO.NET C# POCO 实体生成器和实体框架 4.1 和结构映射作为我们的依赖注入。我们还使用 MvcScaffolding 来生成基于 pocos 的控制器、存储库和接口。到目前为止,一切运行良好,测试用例和模拟非常容易,生成的 pocos 干净且易于使用。我建议您研究一下这个工具。
      【解决方案6】:

      ADO.NET 连接池将在幕后管理连接。您使用多少不同的实体(以及因此具有自己的上下文的存储库)基本上都无关紧要;每个数据库操作都将从同一个池中获取连接。

      存储库的原因是使您能够抽象/替换为测试等创建实体的方式。实体对象可以像普通对象一样实例化,而无需上下文的服务,因此测试存储库会这样做测试数据

      【讨论】:

      • 关于连接池的附加说明:假设所有连接都使用相同的连接字符串。您可以使用的每个单独的连接字符串(即使唯一的区别是增加了空间)都有自己的连接对象池。
      • 所以如果我使用单个数据上下文或使用 10 个数据上下文无关紧要吗?
      • 就连接而言,根本没有可衡量的差异。所有这一切还假设没有人向数据上下文或存储库添加一堆“繁重”的代码。
      • 正确。唯一的问题是 GC 开销,但这太微不足道了,我什至不应该提及它。
      • 不应该提什么? :P
      猜你喜欢
      • 2012-06-11
      • 2016-02-12
      • 1970-01-01
      • 2011-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多