C# 使用单独的域和持久层处理聚合内部的更改

C# 使用单独的域和持久层处理聚合内部的更改,c#,entity-framework-core,domain-driven-design,repository-pattern,C#,Entity Framework Core,Domain Driven Design,Repository Pattern,我对DDD还比较陌生,但我正在尽可能快地死记硬背。我遵循这一指南,了解如何使用Mediatr和EF Core构建代码 幸运的是,对于这个应用程序来说,持久性和域模型是相同的。不幸的是,我的数据层与我们的域模型不匹配,因为它是一个遗留数据库 因此,我将域与持久性分离,这很好。但我很难理解如果我在命令处理程序中执行此代码块(试图使其简单明了) 如何使repo的底层数据库上下文知道所应用的更改。我唯一能想到的就是调用repo.Update(聚合),然后尝试应用db调用来更新db的各个位置 对我来说,这

我对DDD还比较陌生,但我正在尽可能快地死记硬背。我遵循这一指南,了解如何使用Mediatr和EF Core构建代码

幸运的是,对于这个应用程序来说,持久性和域模型是相同的。不幸的是,我的数据层与我们的域模型不匹配,因为它是一个遗留数据库

因此,我将域与持久性分离,这很好。但我很难理解如果我在命令处理程序中执行此代码块(试图使其简单明了)

如何使repo的底层数据库上下文知道所应用的更改。我唯一能想到的就是调用repo.Update(聚合),然后尝试应用db调用来更新db的各个位置

对我来说,这似乎是一种气味

任何见解都会很好

谢谢大家!

编辑: 具有独立域和持久性层的存储库模式应该返回presistance层的模型还是域的模型

例如: 我有一个总公司。我有一个名为CompanyLegacy的数据库表,它在持久性层使用实体框架核心进行建模

我的存储库应该是CompanyLegacyRepository还是CompanyRepository?如果CompanyRepository,这意味着我查询CompanyLegacy表,并将其映射到公司域模型,然后返回它。此模型不会被更改跟踪。这就是我的问题所在

但是,如果我要做一个公司级的GacyRepository,那么它似乎不符合DDD准则,所有操作都要应用到aggregateroot

存储库模式是否应该具有单独的域和持久性 层返回持久层的模型还是域的模型

存储库应该返回您的域模型。如果您在基础架构层中使用DTO(如CompanyLegacy),则存储库负责将它们映射到域模型。请注意,在a中,应用程序层不应该知道基础结构层中使用的DTO。。。域模型是应用程序的核心。看看哪个与你的关系密切

您的存储库应称为
CompanyRepository
。您可以为此存储库定义一个接口,如:

public interface ICompanyRepository
{
    // Company is your domain model not DTO (i.e. your legacy model)
    Company GetById(int id);
    void Add(Company);
    void Update(Company);
}
变更跟踪 实体框架变更跟踪有其局限性,这个问题就是其中一个例子,我们不能依赖EF变更跟踪(因为DTO)。上述存储库的实现类似于:

public CompanyRepository: ICompanyRepository
{
    Private MyDbContext _context;
    public CompanyRepository(MyDbContext myDbContext) { _context = myDbContext; }

    public Company GetById(int id)
    {
        var companyLegacy = _context
            .CompanyLegacy
            .AsNoTracking()
            .Where(c => c.id = id)
            .FirstOrDefault();
        return MyMapper.ToCompany(companyLegacy);
    }

    public void Add(Company company)
    {
        var companyLegacy = MyMapper.ToLegacy(company);
        _context.Add(companyLegacy);
        _context.SaveChanges();
    }

    public void Update(Company)
    {
        var companyLegacy = MyMapper.ToLegacy(company);
        _context.Update(companyLegacy);
        _context.SaveChanges();
    }
}
有助于更高级的操作,您可以找到有关的更多信息


与EF 4/5/6(非核心)相关,但让您了解如何使用唯一标识符来决定是否应添加或更新实体。

这是ORM的工作,例如实体框架、Dapper等。没错,ORM是我持久性层的一部分。GetById将使用dbcontext从db加载数据,并将其映射到域对象。然后addItemToList将插入到所述域对象中。我是否应该执行repo.Update(聚合)以使用ORM来确定更改了什么?请标记您正在使用的ORM。另外,听起来你的问题与使用某种ORM而不是DDD保存数据更相关,最好能澄清这个问题。完成,谢谢@hoomanbahreiniy你的代码对我来说不合适
GetById(1234)
应该返回一个实体。。。通常,您将项目添加到
列表
而不是
实体
。但是为了回答您的问题,EntityFramework有一个跟踪选项,如果您使用它,可以跟踪对实体所做的更改,并且在保存更改时,EF将更新DB。感谢您提供了这个完整的答案。这验证了我在正确的道路上,因为我正在以这种方式工作。我的一个小问题是,如果一个聚合具有一些关系属性(1到多个关系),理论上我可以为各种聚合拥有多个存储库。例如,CompanyEmployeeRepository和CompanySalesRepository。这有意义吗?通过这种方式,我可以为手头的任务创建一个经过微调的聚合,而不是一个需要更新一堆表的销售人员和员工聚合。名为“更改跟踪”的部分很混乱,因为代码不进行更改跟踪。此代码将更新数据库中的所有列,而不考虑公司中的更改。我总是发现让EF正确更新相关实体(特别是删除)非常棘手,除非您非常了解EF的工作原理,否则最终会出现令人讨厌的错误(未删除的实体、孤立的实体等)。所以,我更喜欢更安全的方法。@MatthewHartz如果你使用这些存储库,你将无法达到聚合的目的。聚合是事务边界。聚合必须确保整个状态有效(根+所有关系)。为此,所有更改都必须通过聚合根进行,并以事务方式存储。如果您有一个存储库,允许您在不经过根目录的情况下修改聚合的实体,那么您就不再有聚合了。此外,您应该在根目录上添加乐观并发检查,并始终修改根目录,即使只是为了增加版本列,否则并发更新最终会破坏聚合的一致性。@MatthewHartz:这取决于您如何实现您的。。。例如,您的
公司
实体可以有一个
列表
。。。或者您的
员工
实体可以拥有
公司
财产。。。如果您选择使用这两种解决方案中的任何一种,那么您就不需要
CompayToEmployee
实体。。。在这种情况下,最好不要创建额外的实体。但要回答您的问题,是的,每个实体需要一个存储库(或者更准确地说,每个聚合需要一个存储库)
public CompanyRepository: ICompanyRepository
{
    Private MyDbContext _context;
    public CompanyRepository(MyDbContext myDbContext) { _context = myDbContext; }

    public Company GetById(int id)
    {
        var companyLegacy = _context
            .CompanyLegacy
            .AsNoTracking()
            .Where(c => c.id = id)
            .FirstOrDefault();
        return MyMapper.ToCompany(companyLegacy);
    }

    public void Add(Company company)
    {
        var companyLegacy = MyMapper.ToLegacy(company);
        _context.Add(companyLegacy);
        _context.SaveChanges();
    }

    public void Update(Company)
    {
        var companyLegacy = MyMapper.ToLegacy(company);
        _context.Update(companyLegacy);
        _context.SaveChanges();
    }
}