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

时间:2009-09-30 00:09:31

标签: c# .net asp.net asp.net-mvc repository-pattern

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

所以我猜我应该把它们分组。但是我不确定如何将它们分组。

现在我建立了一个注册存储库来处理我的所有注册内容。然而,在我有3个存储库之前,我需要更新4个表。

例如,其中一个表是许可证表。当他们注册时,我查看他们的密钥并检查它是否存在于数据库中。现在如果我需要在注册以外的其他位置检查该许可证密钥或该表中的其他内容会发生什么?

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

那么在这种情况下我该怎么做?再次重写代码(打破DRY)?尝试将这两个存储库混合在一起并希望在其他一些时间点不需要任何方法(例如,我可能有一个检查userName是否被使用的方法 - 也许我会在其他地方需要它。)

另外,如果我将它们合并在一起,我要么需要2个服务层才能进入同一个存储库,因为我认为网站的2个不同部分的所有逻辑都很长,我必须有像ValidateLogin()这样的名称, ValdiateRegistrationForm(),ValdiateLoginRetrievePassword()等。

或者无论如何都要调用存储库,只是有一个奇怪的声音名称?

似乎很难创建一个具有足够通用名称的存储库,因此您可以将它用于应用程序的许多位置并且仍然有意义,我不认为在存储库中调用另一个存储库是一个好习惯吗?

7 个答案:

答案 0 :(得分:39)

在使用存储库模式时我做错了一件事 - 就像你一样,我认为该表与存储库1:1有关。当我们从域驱动设计中应用一些规则时 - 分组存储库问题经常会消失。

存储库应该是Aggregate root而不是表。这意味着 - 如果实体不应该独自生活(即 - 如果你有Registrant参与特定的Registration) - 它只是一个实体,它不需要一个存储库,它应该更新/通过它所属的聚合根存储库创建/检索。

当然 - 在许多情况下,这种减少存储库数量的技术(实际上 - 它更像是一种构建域模型的技术)无法应用,因为每个实体都应该是一个聚合根(这在很大程度上取决于你的域名,我只能盲目猜测)。在您的示例中 - License似乎是一个聚合根,因为您需要能够在没有Registration实体的上下文的情况下检查它们。

但这并不限制我们级联存储库(如果需要,Registration存储库可以引用License存储库。这并不限制我们直接从License对象引用Registration存储库(最好是通过IoC)。

尽量不要通过技术提供的复杂性或误解某些东西来推动您的设计。仅仅因为您不想构建2个存储库而在ServiceX中对存储库进行分组并不是一个好主意。

更好的是给它一个正确的名字 - RegistrationService

但是一般应该避免服务 - 它们往往是导致anemic domain model的原因。

修改
开始使用IoC。它真正减轻了注入依赖性的痛苦 而不是写:

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

你可以写:

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

P.S。最好使用所谓的common service locator但这只是一个例子。

答案 1 :(得分:5)

我开始做的一件事就是实际开发包装N个存储库的服务。希望您的DI或IoC框架可以帮助您更轻松。

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

这有意义吗?此外,据我所知,在这个庄园中提供服务可能会或可能不会实际符合DDD原则,我只是这样做,因为它似乎有效。

答案 2 :(得分:2)

我正在做的是我有一个定义如下的抽象基类:

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> 

等...

我需要单独的存储库,因为我们对某些存储库有限制,这为我们提供了最大的灵活性。希望这会有所帮助。

答案 3 :(得分:2)

我有这个作为我的存储库类,是的,我扩展到表/区域存储库,但我有时不得不打破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类创建的datacontext。

所以现在我有一个标准的存储库实现标准调用,如All和Find,在我的AdminRepository中我有特定的调用。

虽然我不认为,但是没有回答DRY的问题。

答案 4 :(得分:1)

使用FluentNHibernate的通用Repository实现的

Here is an example。它能够持久保存您为其编写映射器的任何类。它甚至能够基于映射器类生成数据库。

答案 5 :(得分:1)

存储库模式是一种糟糕的设计模式。我使用许多旧的.Net项目,这种模式通常会导致“分布式事务”,“部分回滚”和“连接池耗尽”错误,这些错误可以避免。问题是模式尝试在内部处理连接和事务,但这些应该在Controller层处理。 EntityFramework也已经抽象了很多逻辑。我建议使用服务模式来重用共享代码。

答案 6 :(得分:0)

我建议你看看Sharp Architecture。他们建议每个实体使用一个存储库。我目前在我的项目中使用它,并对结果非常满意。