ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

现在我创建了一个注册存储库来处理我所有的注册内容。但是,在我有 3 个存储库来执行此操作之前,我需要更新 4 个表。

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

一个地方可能是登录(检查密钥是否未过期)。

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

此外,如果我将它们合并在一起,我要么需要 2 个服务层进入同一个存储库,因为我认为拥有一个站点的 2 个不同部分的所有逻辑会很长,而且我必须有像 ValidateLogin()、ValdiateRegistrationForm() 这样的名称,valdiateLoginRetrievePassword() 等

或者无论如何都调用存储库并且只是有一个听起来很奇怪的名字?

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

+1。好问题。
谢谢它已经困扰我一段时间了。因为我发现我在书中看到的那些太简单了,所以它们没有告诉你在这些情况下该怎么做。
我基本上和你做的一样,是的,如果我有一个在多个存储库中使用的 linq2sql 类,我需要更改我破坏 DRY 的表结构。不太理想。我现在计划得更好一点,所以我不需要多次使用 linq2sql 类,我想这是很好的分离问题,但我预见有一天这对我来说将是一个真正的问题。
我在这里问了一个类似的问题(但不相同):stackoverflow.com/questions/910156/…
是的,但似乎很难针对所有情况进行计划。就像我说的那样,我可以将登录和注册合并到一个身份验证中,并有 2 个单独的层。这可能会解决我的问题。但是,如果在说我网站的个人资料页面时我想以某种原因向他们展示他们的密钥(也许我会让 htem 更改它或其他什么)会发生什么。现在我该怎么做才能打破 DRY 并写同样的东西?或者尝试创建一个存储库,以某种方式将所有这 3 个表都放入一个好名字。

d
dpp

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

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

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

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

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

最好给它一个合适的名称 - RegistrationService

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

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

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

你将能够写:

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

Ps 使用所谓的 common service locator 会更好,但这只是一个例子。


哈哈哈...我正在建议使用服务定位器。总是很高兴看到我过去有多愚蠢。
@Arnis 听起来您的观点似乎发生了变化-我很感兴趣您现在将如何以不同的方式回答这个问题?
自从我回答这个问题以来,@ngm 发生了很多变化。我仍然同意聚合根应该绘制事务边界(作为一个整体保存),但我对使用存储库模式抽象持久性不太乐观。最近 - 我只是直接使用 ORM,因为像急切/延迟加载管理这样的东西太尴尬了。专注于开发丰富的领域模型而不是专注于抽象持久性更有益。
@Developer 不,不完全是。他们仍然应该坚持无知。您从外部检索聚合根并在其上调用完成该工作的方法。我的域模型有零引用,只有标准的 .net 框架。要实现这一点,您必须拥有足够智能的丰富领域模型和工具(NHibernate 可以做到这一点)。
相反。获取多个聚合通常意味着您可以使用 PK 或索引。比利用 EF 生成的关系快得多。根聚合越小,就越容易在它们上获得性能。
n
neouser99

为了解决这个问题,我开始做的一件事是实际开发包装 N 个存储库的服务。希望您的 DI 或 IoC 框架可以帮助使这更容易。

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

那有意义吗?另外,我知道在这个庄园里谈到服务可能会或可能不会真正符合 DDD 原则,我这样做只是因为它似乎有效。


不,这没有多大意义。我目前不使用 DI 或 IoC 框架,因为我的盘子已经足够了。
如果您可以仅使用 new() 实例化您的存储库,您可以尝试这个... public ServiceImpl() : this(new Repo1, new Repo2...) {} 作为服务中的附加构造函数。
依赖注入?我已经这样做了,但仍然不确定你的代码是什么以及它解决了什么。
我专门针对存储库的合并。哪个代码没有意义?如果您使用的是 DI,那么答案中的代码将在您的 DI 框架将注入那些 IRepo 服务的意义上起作用,在注释代码中,它基本上只是一个小工作来做 DI(基本上你的无参数构造函数“注入”这些依赖项到您的 ServiceImpl 中)。
我已经使用 DI,所以我可以更好地进行单元测试。我的问题是,如果您将服务层和 repos 设置为详细的名称。然后,如果您需要在其他任何地方使用它们,它会看起来很奇怪,例如在其他类中调用 RegRepo 的 RegistrationService 层,例如 ProfileClass。所以我没有从你的例子中看到你在做什么。就像如果您在同一个服务层中开始有太多的 Repos,那么您将拥有如此不同的业务逻辑和验证逻辑。由于在服务层中,您通常会放入验证逻辑。这么多我需要更多...
M
Michael Mann

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

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> 

ETC....

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


所以你试图建立一个通用存储库来处理这一切?
那么实际上要说的是 Update 方法。就像你喜欢这个 V 更新一样,它传入了一个 T entitytoUpdate 但实际上没有代码更新它,还是存在?
是的.. update 方法中会有代码,因为您将编写一个从通用存储库下降的类。实现代码可以是 Linq2SQL 或 ADO.NET 或任何您选择的数据访问实现技术
g
griegs

我有这个作为我的存储库类,是的,我在表/区域存储库中进行了扩展,但有时我仍然必须打破 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 类创建的数据上下文。

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

尽管我不认为,但没有回答 DRY 的问题。


什么“存储库”?和 CreateInstance 一样吗?不知道你所拥有的一切都在做什么。
它像任何东西一样通用。基本上,您需要为您的(区域)有一个特定的存储库。查看上面对我的 AdminRepository 的编辑。
J
James Jones

Here is an example 使用 FluentNHibernate 的通用存储库实现。它能够持久化您为其编写映射器的任何类。它甚至能够根据映射器类生成您的数据库。


S
Sly

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


我会在这里给 +1,但他表示 DI 或 IoC 容器不是一个完全的选择(当然这些不是 Sharp Arch 的唯一好处)。我猜他正在处理很多现有代码。
什么被视为实体?那是整个数据库吗?或者那是一个数据库表? DI 或 IoC 容器目前不是一个选项,因为我只是不想在我同时学习的其他 10 件事之上学习它。它们是我将在我的网站的下一个版本或我的下一个项目中研究的东西。尽管我不确定它是否会是这个网站的快速浏览,它似乎希望你使用 nhirbrate,而我目前正在使用 linq to sql。
C
ColacX

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