ASP.NET MVC使用Repository Pattern

时间:2010-09-30 09:39:47

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

目前我正在使用EF并在我的所有动作中直接使用其datacontext,但是因为我开始阅读松耦合和可测试性,我认为这不是最好的方法。在我开始重构所有当前代码之前,我试图理解所有专业人员和骗子。

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

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

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

6 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:3)

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

答案 2 :(得分:3)

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

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

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

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

实际上我使用带有Linq to SQL的MVC的存储库作为我的后备存储,它允许我在实际的后备存储上有一定程度的灵活性,所以我在BL中使用手工制作的L2S对象,这些对象具有其他字段和功能坚持到后盾店。这样我就可以从L2S方面获得一些很棒的功能,更改跟踪,对象层次结构等,同时还允许我用模拟的存储库代替TDD。

答案 3 :(得分:2)

在确定使用实体作为业务对象时遇到的困难时,您会发现问题。经过多次试验和错误,这是我们已经解决的模式,这对我们来说非常有效:

我们的应用程序分为模块,每个模块分为三层:Web(前端),Core(业务)和Data。在我们的例子中,这些层中的每一层都有自己的项目,因此存在强制执行,导致我们的依赖关系无法紧密耦合。

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

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

数据层的全部职责是实现Core层中定义的存储库接口。它有一个实体框架上下文代表我们的数据存储,但它不返回实体(技术上是“数据”对象),而是返回Core层中定义的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);
}

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

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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

ADO.NET连接池将管理幕后连接。根本不重要的是你使用多少个不同的实体(因此有自己的上下文的存储库);每个数据库操作都将从同一个池中获取连接。

存储库的原因是使您能够抽象/替换实体的创建方式以进行测试等。实体对象可以像普通对象一样实例化,而不需要Context的服务,因此测试存储库可以实现测试数据