我有一个ASP.NET网站,它使用域驱动设计并使用存储库进行数据库操作 我想知道单例存储库和静态存储库以及简单的存储库类的优缺点是什么,它们在每次访问时都是新的? 更进一步,如果有人可以比较和指导我使用其中哪一个我将不胜感激。
答案 0 :(得分:15)
静态和单例不是Repository模式的好解决方案。如果您的应用程序将来会使用2个或更多存储库,该怎么办?
IMO最好的解决方案是使用Dependency Injection容器并在需要它的类中注入您的IRepository接口。
我建议你阅读一本关于域驱动设计的好书和一本关于依赖注入的好书(比如Mark Seeman的Dependency Injection in .NET)。
您有两个单例存储库:
class Repository<TEntity> {
static Repository<TEntity> Instance { get { ... /*using sql server*/ } }
}
class Repository2<TEntity> {
static Repository2<TEntity> Instance { get { ... /*using WCF or XML or any else */ } }
}
使用它们的服务必须具有对其中一个或两者的静态引用:
class OrderService {
public void Save(Order order) { Repository<Order>.Instance.Insert(order); }
}
如果存储库是静态引用的,如何使用Repository2保存订单?
更好的解决方案是使用DI:
interface IRepository<TEntity> { ... }
class SqlRepository<TEntity> : IRepository<TEntity> { ....}
class OrderService {
private readonly IRepository<TEntity> _repo;
public OrderService(IRepository<TEntity> repo) { _repo = repo; }
public void Save(Order order) { repo.Insert(order); }
}
答案 1 :(得分:6)
由于以下原因,请勿使用静态或单例存储库:
它会影响测试性,在单元测试时无法模拟它。
它会影响可扩展性,您不能创建多个具体实现,并且无法在不重新编译的情况下替换行为。
它影响生命周期管理方面的可扩展性,不依赖依赖注入框架来注入依赖关系和管理生命周期。
它影响可维护性,它强制依赖具体实现而不是抽象。
底线:不要使用静态或单例存储库
而是在域模型项目中创建存储库接口,并在具体的数据访问项目中实现这些接口,并使用依赖注入框架。
答案 2 :(得分:4)
两个SOLID原因没有单例存储库:
存储库的使用者将耦合到存储库实现。这将对可扩展性和可测试性产生负面影响。这是DIP违规行为。取决于抽象,而不是结核。
存储库实现必须违反SRP,因为它很可能最终会管理ORM会话,数据库连接和潜在的事务。它还必须提供线程安全保证,因为它可能可以从多个线程使用。相反,应该将数据库连接(ORM会话)注入到存储库实现中,以便使用代码可以将多个存储库调用安排到事务中。
这两个问题的可能解决方案是Constructor Injection。
答案 3 :(得分:3)
我个人不同意以前的答案。
我开发了多个网站(每月有7百万次网页浏览),我的静态存储库从来没有遇到任何问题。
我的静态存储库实现非常简单,只包含对象提供程序作为属性。单个存储库可以包含所需数量的提供程序。
然后,提供者负责管理数据库连接和事务。使用TransactionScope,消费者可以管理交易或将其交给提供者。
每个提供者都使用单例模式开发。
这样,我可以通过简单地调用它来获取我的对象:
var myObj = MyRepository.MyProvider.GetMyObject(id);
在我的应用程序的每个Web池中,每个类型只有一个存储库和一个提供程序。根据您在网站上同时拥有的用户数量,您可以设置多个Web池(但大多数时候只有一个足够)。
我没有看到我的存储库/提供者使用者与我的存储库耦合的位置。实际上,我的提供者的实现完全是从它们中抽象出来的。当然,我的存储库返回的所有提供程序都是接口,我可以随时轻松更改它们的实现,并将我的新dll推送到Web服务器上。如果我想创建一个具有相同界面的全新提供者,我只需要在一个地方更改它:我的存储库。
这样,无需添加依赖注入或必须创建自己的ControllerFactory(用于MVC procjects)。
您仍然可以在控制器中使用干净的代码。每次请求页面时(通常在ControllerFactory中使用反射),您还将节省大量的存储库创建和销毁。
如果您正在寻找可扩展的解决方案(如果您确实需要它,而大部分时间并不是真正的问题),那么与依赖注入相比,我开发存储库的方式永远不会成为问题。