目前我正在使用EF并在我的所有动作中直接使用其datacontext,但是因为我开始阅读松散耦合和可测试性,我认为这不是最好的方法。在我开始重构所有当前代码之前,我试图理解所有的专家和骗子。
问题1:
考虑到每个实体都需要自己的存储库,因此必须建立自己与数据源的连接(假设使用EF的数据库),如果我需要来自单个页面上5个不同实体的数据,那么会产生很多开销吗?
问题2:
我在网上找到的所有例子中都看到的是,大多数人(甚至像shanselman这样的人)使用由LINQ或EF生成的实体类来实现存储库模式,这不会破坏存储库模式的目的。关于松耦合?另一方面,使用POCO类与AutoMapper结合使用的替代方案是什么? (这让我有点害怕)
我希望有一些人可以对此有所了解,因为如果存储库模式是网站的正确选择,我现在有点困惑。
你可以阅读 这本书。有一个使用Repository模式和LINQ的好例子。
还有这篇文章 使用存储库和工作单元模式与Entity Framework 4.0。
你可以阅读 这本书。有一个使用Repository模式和LINQ的好例子。
还有这篇文章 使用存储库和工作单元模式与Entity Framework 4.0。
ObjectContext使用连接池,因此它不会像您想象的那样低效。此外,SQL服务器(即MSSQL)确实针对大量并发连接进行了优化。
至于如何实现它,我会使用一些IRepository接口。然后,您可以创建特定的接口,即PostRepository> IRepository,最后,在具体的类中实现它(例如,一个真正的类,以及一个用于测试的虚假内存)。
首先,我不知道每个实体都需要拥有它自己的存储库,所以我会破坏这个限制。
对于Scott H的实现,我假设您指的是Nerd Dinner应用程序,他自己承认并不是真正的存储库模式。
正如您所推测的那样,存储库模式的目标是将数据存储与其上方的层隔离。它不仅仅是出于测试原因,还允许您在不影响UI /业务逻辑的情况下更改后备存储。
在纯粹的术语中,您将创建POCO,您将从存储库返回到BL,通过使用接口来定义您可以传递的存储库合约并使用接口而不是具体实现。这将允许您传入任何实现Repository接口的对象,无论是您的实时存储库还是模拟存储库。
实际上我使用带有Linq to SQL的MVC的存储库作为我的后备存储,这使我在实际的后备存储上具有一定程度的灵活性,因此我在BL中使用手工制作的L2S对象,这些对象具有不持久的其他字段和功能到后盾商店。这样我就可以从L2S方面获得一些很棒的功能,更改跟踪,对象层次结构等,同时还允许我用模拟的存储库代替TDD。
在识别使用实体作为业务对象的难度时,您可以在头上敲击。经过多次试验和错误,这是我们已经解决的模式,这对我们来说非常有效:
我们的应用程序分为模块,每个模块分为三层:Web(前端),Core(业务)和Data。在我们的例子中,这些层中的每一层都有自己的项目,因此存在强制执行,导致我们的依赖关系无法紧密耦合。
该 核心 layer包含实用程序类,POCO和存储库接口。
该 卷筒纸 layer利用这些类和接口来获取所需的信息。例如,MVC控制器可以将特定的存储库接口作为构造函数参数,因此我们的IoC框架在创建控制器时会注入该存储库的正确实现。存储库接口定义了返回POCO对象的选择器方法(也在Core业务层中定义)。
该 数据 layer的全部职责是实现Core层中定义的存储库接口。它有一个实体框架上下文,代表我们的数据存储,但它不返回实体(技术上是“数据”对象),而是返回Core层(我们的“业务”对象)中定义的POCO。
为了减少重复,我们有一个抽象的,通用的 EntityMapper
class,提供将实体映射到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在它们的构造函数中做的很少,所以如果一个控制器有多个存储库依赖项就没关系。实体框架不仅重用连接,而且实体上下文本身仅在调用其中一个存储库方法时创建。
每个模块也有一个特殊的 测试 project,包含这三个类中的类的单元测试。我们甚至想出了一种方法来使我们的存储库和其他数据访问类在某种程度上可以进行单元测试。现在我们已经设置了这个基本的基础架构,为我们的Web应用程序添加功能通常非常顺利,并且不会太容易出错。
问题2:避免这种情况的一种方法是使用类似“ADO.NET C#POCO实体生成器”。
ADO.NET连接池将管理幕后连接。根本不重要的是你使用了多少个不同的实体(因此有自己的上下文的存储库);每个数据库操作都将从同一个池中获取连接。
存储库的原因是使您能够抽象/替换实体的创建方式以进行测试等。实体对象可以像没有Context服务的普通对象一样实例化,因此测试存储会为测试数据执行此操作