如何正确使用存储库模式?
我想知道我应该如何分组我的仓库? 就像我在asp.net mvc和我的书中看到的例子,他们基本上每个数据库表使用一个存储库。 但是,这看起来像很多的知识库,导致你不得不在稍后调用许多存储库进行嘲弄和调整。
所以我猜我应该把他们分组。 但是我不知道如何分组。
现在我做了一个注册库来处理我所有的注册内容。 不过,我需要更新4个表格,之前我有3个库来做到这一点。
例如,其中一个表是许可证表。 当他们注册我看他们的密钥,并检查它,看看是否存在数据库中。 现在如果我需要检查这个许可证密钥或其他地方的其他地方,然后registry中的其他东西,会发生什么?
一个地方可以login(检查密钥是否没有过期)。
那么在这种情况下我会做什么? 重写代码(中断DRY)? 尝试将这两个存储库一起处理,并希望在其他某个时间点不需要任何方法(例如,也许我可能有一个方法来检查用户名是否被使用 – 也许我需要在别的地方)。
另外如果我把它们合并在一起,我会需要2个服务层去同一个存储库,因为我认为有一个网站的2个不同部分的所有逻辑将是漫长的,我必须有像ValidateLogin(),ValdiateRegistrationForm() ,ValdiateLoginRetrievePassword()等。
或者调用Repository,只是有一个怪异的名称?
创build一个具有足够通用名称的存储库似乎很难,因此您可以将其用于应用程序的许多位置,并且仍然有意义,我不认为在存储库中调用另一个存储库会是一个好的做法吗?
有一件事,当我使用存储库模式时,我做错了 – 就像你一样,我认为这个表涉及到存储库1:1。 当我们应用领域驱动devise中的一些规则 – 分组存储库问题通常会消失。
存储库应该是每个聚合根,而不是表。 这意味着 – 如果实体不应该单独生活(即 – 如果你有一个Registrant
参与特定的Registration
) – 它只是一个实体,它不需要一个存储库,应该通过聚合存储库更新/创build/检索它属于的根。
当然,在许多情况下,减less存储库数量的这种技术(实际上 – 这更多是一种构build域模型的技术)不能应用,因为每个实体都应该是一个聚合根(高度依赖于您的域,我只能提供盲目的猜测)。 在你的例子 – License
似乎是一个聚合根,因为你需要能够检查他们没有上下文的Registration
实体。
但是这不会限制我们级联存储库(如果需要, Registration
库允许引用License
存储库)。 这并不限制我们直接从Registration
对象引用License
存储库(最好 – 通过IoC)。
只是尽量不要通过技术提供的复杂或误解的东西来驱动你的devise。 在ServiceX
存储库进行分组只是因为您不想构build2个存储库不是个好主意。
更好的是给它一个适当的名称 – RegistrationService
即
但是一般应避免服务 – 它们往往是导致贫血域模型的原因 。
编辑:
开始使用IoC。 它真正减轻了注入依赖的痛苦。
而不是写作:
var registrationService = new RegistrationService(new RegistrationRepository(), new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());
你将能够写:
var registrationService = IoC.Resolve<IRegistrationService>();
PS会更好地使用所谓的通用服务定位器,但这只是一个例子。
我已经开始做的一件事情是要真正开发包装N仓库的服务。 希望您的DI或IoC框架可以帮助简化这一过程。
public class ServiceImpl { public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { } }
那有意义吗? 另外,据我所知,在这个庄园里谈到服务可能实际上也可能不符合DDD原则,我只是这样做,因为它似乎工作。
我正在做的是我有一个抽象的基类定义如下:
public abstract class ReadOnlyRepository<T,V> { V Find(T lookupKey); } public abstract class InsertRepository<T> { void Add(T entityToSave); } public abstract class UpdateRepository<T,V> { V Update(T entityToUpdate); } public abstract class DeleteRepository<T> { void Delete(T entityToDelete); }
然后,您可以从抽象基类中派生出您的存储库,并在通用参数不同的情况下延长您的单个存储库;
public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>, ReadOnlyRepository<string, IRegistrationItem>
等等….
我需要单独的存储库,因为我们对一些存储库有限制,这给了我们最大的灵活性。 希望这可以帮助。
我有这个作为我的存储库类,是的,我扩展在表/区域存储库,但我仍然有时不得不打破DRY。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MvcRepository { public class Repository<T> : IRepository<T> where T : class { protected System.Data.Linq.DataContext _dataContextFactory; public IQueryable<T> All() { return GetTable.AsQueryable(); } public IQueryable<T> FindAll(Func<T, bool> exp) { return GetTable.Where<T>(exp).AsQueryable(); } public T Single(Func<T, bool> exp) { return GetTable.Single(exp); } public virtual void MarkForDeletion(T entity) { _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity); } public virtual T CreateInstance() { T entity = Activator.CreateInstance<T>(); GetTable.InsertOnSubmit(entity); return entity; } public void SaveAll() { _dataContextFactory.SubmitChanges(); } public Repository(System.Data.Linq.DataContext dataContextFactory) { _dataContextFactory = dataContextFactory; } public System.Data.Linq.Table<T> GetTable { get { return _dataContextFactory.GetTable<T>(); } } } }
编辑
public class AdminRepository<T> : Repository<T> where T: class { static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString); public AdminRepository() : base( dc ) { }
我也有一个使用Linq2SQL.dbml类创build的datacontext。
所以现在我有一个标准的存储库实现标准调用像所有和查找和我的AdminRepository我有特定的调用。
虽然我不认为,但是不回答DRY的问题。
这是一个使用FluentNHibernate的通用Repository实现的例子 。 这是能够坚持任何类,你已经写了一个映射器。 它甚至能够根据mapper类生成数据库。
Repository模式是一个糟糕的devise模式。 我和许多旧的.Net项目一起工作,这种模式通常会导致“分布式事务”,“部分回滚”和“连接池耗尽”错误,这是可以避免的。 问题是该模式试图在内部处理连接和事务,但是这些应该在Controller层处理。 另外EntityFramework已经抽象了很多的逻辑。 我会build议使用服务模式,而不是重用共享代码。
我build议你看看夏普build筑 。 他们build议每个实体使用一个存储库。 我目前在我的项目中使用它,并对结果感到满意。