如何正确使用存储库模式?

我想知道我应该如何分组我的存储库?就像我在 asp.net mvc 上看到的例子一样,在我的书中,他们基本上每个数据库表使用一个存储库。但是,这看起来像是很多存储库导致您不得不在以后调用许多存储库来进行嘲笑之类的操作。

所以我猜我应该把它们分组,但是我不知道如何分组。

现在我做了一个注册仓库来处理我所有的注册事宜。然而,我需要更新大约4个表,在我有3个仓库来做这件事之前。

例如,其中一个表是许可证表。当他们注册时,我查看他们的密钥并检查它是否存在于数据库中。现在,如果我需要检查这个许可证密钥或其他东西从该表在其他地方,然后注册?

一个点可以是登录(检查密钥是否过期)。

这种情况下我该怎么办?再次重写代码(中断 DRY) ?尝试将这两个存储库合并在一起,并希望在其他时间点不需要这些方法(比如我可能有一个检查是否使用了 userName 的方法——也许我将在其他地方需要它)。

而且,如果我把它们合并在一起,我需要两个服务层到同一个存储库,因为我认为一个站点的两个不同部分的所有逻辑都很长,我必须有像 ValidateLogin () ,ValdiateRegistrationForm () ,ValdiateLoginRetrievePassword ()等名称。

或者给仓库打个电话,起个听起来怪怪的名字?

要创建一个具有足够通用名称的存储库,以便您可以在应用程序的许多地方使用它,并且仍然有意义,这似乎很困难,而且我不认为在存储库中调用另一个存储库是一种好的做法?

19058 次浏览

One thing I've started doing to address this is to actually develop services that wrap N repositories. Hopefully your DI or IoC frameworks can help to make that easier.

public class ServiceImpl {
public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

Does that make sense? Also, I understand that speaking of services in this manor may or may not actually comply with DDD principles, I just do it because it seems to work.

What I am doing is I have a abstract base class defined as follows:

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);
}

You can then derive you repository from the abstract base class and extend your single repository as long at the generic arguments differ for example;

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
ReadOnlyRepository<string, IRegistrationItem>

etc....

I need the separate repositories because we do have restrictions on some of our repositories and this gives us maximum flexibility. Hope this helps.

I have this as my repository class and yeah I extend in the table / area repository but still I sometimes have to break 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>(); }
}


}
}

EDIT

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 )
{
}

I also have a datacontext which was created using Linq2SQL.dbml class.

So now I have a standard repository implementing standard calls like All and Find and in my AdminRepository I have specific calls.

Doesn't answer the question of DRY though I don't think.

I suggest you to look at Sharp Architecture. They suggest using one repository per entity. I'm using it currently in my project and wery pleased with results.

One thing that i did wrong when played around with repository pattern - just like you, i thought that table relates to repository 1:1. When we apply some rules from Domain Driven Design - grouping repositories problem often disappears.

Repository should be per Aggregate root and not table. It means - if entity shouldn't live alone (i.e. - if you have a Registrant that participates in particular Registration) - it's just an entity, it doesn't need a repository, it should be updated/created/retrieved through repository of aggregate root it belongs.

Of course - in many cases, this technique of reducing count of repositories (actually - it's more a technique to structure your domain model) can't be applied because every entity is supposed to be an aggregate root (that highly depends on your domain, I can provide blind guesses only). In your example - License seems to be an aggregate root because you need to be able to check them with no context of Registration entity.

But that does not restrict us to cascade repositories (Registration repository is allowed to reference License repository if needed). That does not restrict us to reference License repository (preferable - through IoC) directly from Registration object.

Just try not to drive your design through complications provided by technologies or misunderstanding something. Grouping repositories in ServiceX just because you don't want to construct 2 repositories ain't good idea.

Much better would be to give it a proper name - RegistrationService i.e.

But services should be avoided in general - they often are cause that leads to anemic domain model.

EDIT:
Do start to use IoC. It truly eases the pain of injecting dependencies.
Instead of writing:

var registrationService = new RegistrationService(new RegistrationRepository(),
new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

you will be able to write:

var registrationService = IoC.Resolve<IRegistrationService>();

P.s. Would be better to use so called common service locator but that's just an example.

Here is an example of a generic Repository implementation using FluentNHibernate. It is capable of persisting any class that you have written a mapper for. It is even capable of generating your database based off of the mapper classes.

The Repository pattern is a bad design pattern. I work with many old .Net projects and this pattern typically causes "Distributed Transactions", "Partial Rollback" and "Connection Pool Exhausted" errors which could be avoided. The problem is that the pattern tries to handle connections and transactions internally but those should be handled at the Controller layer. Also EntityFramework already abstracts a lot of the logic. I would suggest using the Service pattern instead to reuse shared code.