简体   繁体   English

如何在工作单元中处理数据库生成的ID值

[英]How do I deal with database generated id values in a unit of work

Caveat I am using SQL Server 2008 R2 as the persistence framework. 请注意,我使用SQL Server 2008 R2作为持久性框架。 I do not have the option of changing this. 我没有更改此选项。 I am using a Micro-ORM for persistence. 我正在使用Micro-ORM进行持久化。 I also do not have the option of changing this. 我也没有选择更改此选项。 Thirdly, I do not have the option of changing the table identity columns to guids. 第三,我没有选择将表标识列更改为向导。 They have to remain as integers. 它们必须保留为整数。

The aggregate I want to create is a project that has a few tasks. 我要创建的聚合是一个有一些任务的项目。 I want to persist this to two tables, one with the parent project and the other with the tasks, linked by the parent project id. 我想将其保存到两个表,一个表与父项目,另一个与任务,由父项目ID链接。 Simple enough. 很简单。 Obviously the table structure is a persistence detail that I would like to keep out of the domain model. 显然,表结构是一个持久性细节,我想将其保留在域模型之外。 Once I persist this, I want to send the project's id back to the user so that they may navigate to the newly created project. 坚持这一点后,我想将项目的ID发送回用户,以便他们可以导航到新创建的项目。

public class CreateNewProjectRequest
{
    public int InterestingInteger { get; set; }
    public string MoreDataNeededForRequest { get; set; }
}

public CreateProjectApplicationService
{
    private readonly IUnitOfWork uow;

    public CreateProjectApplicationService(IUnitOfWork uow)
    {
        this.uow = uow;
    }

    public int CreateNewProject(CreateNewProjectRequest request)
    {
        var project = ProjectAggregate.CreateFrom(request);
        var projectRepository = new ProjectRepository(uow);
        projectRepository.Add(project);
        projectRepository.Save(project);
        //db transaction commits
        uow.Commit();
    }
}

Referencing several resources on this topic (Vaughn Vernon's DDD book and the latest Wrox DDD book Patterns, Principles, and Practices Of Domain-Driven Design), I see that the repository is responsible for persistence of the aggregate root. 引用有关该主题的一些资源(沃恩·弗农(Vaughn Vernon)的DDD书和最新的Wrox DDD书《域驱动设计的模式,原理和实践》,我发现存储库负责聚合根的持久性。 The repository interface lives in the domain model. 存储库接口位于域模型中。

In the Wrox DDD book, a UnitOfWork and a IUnitOfWorkRepository exist and are defined as follows: 在Wrox DDD书中,存在UnitOfWorkIUnitOfWorkRepository ,它们的定义如下:

public interface IUnitOfWork
{
    void RegisterAmended(IAggregateDataModel entity, IUnitOfWorkRepository unitofWorkRepository);
    void RegisterNew(IAggregateDataModel entity, IUnitOfWorkRepository unitofWorkRepository);
    void Commit();
    void Clear();
}

public interface IUnitOfWorkRepository
{
    void PersistCreationOf(IAggregateDataModel entity);
    void PersistUpdateOf(IAggregateDataModel entity);
    void PersistDeleteOf(IAggregateDataModel entity);
}

public class ProjectRepository : IProjectRepository, IUnitOfWorkRepository
{
    //Simplified for example's sake, I didn't implement all of IUnitOfWorkRepository
    private readonly IUnitOfWork uow;

    public ProjectRepository(IUnitOfWork uow)
    {
        this.uow = uow;
    }

    public void Add(Project project)
    {
        unitOfWork.RegisterNew(project, this);
    }

    public void PersistCreationOf(IAggregateDataModel entity)
    {
        //Persist changes via micro ORM -- new id is available
    }
}

The implementation for commit created a transaction and persists it. 提交的实现创建了一个事务并将其持久化。

The issue I am having is that when the Commit() method is called to persist the transaction to the data store, I have a new ID that was created in the process. 我遇到的问题是,当调用Commit()方法将事务持久化到数据存储区时,我有一个在该过程中创建的新ID。 I understand that CreateProjectApplicationService might touch multiple aggregates (in my case it does not... yet), so the unit of work implementation manages the transaction here so the data store is in a consistent state. 我知道CreateProjectApplicationService可能涉及多个聚合(在我看来,它还没有...),因此工作单元实现在此处管理事务,因此数据存储处于一致状态。

I can't simply just seed the id from the database. 我不能只是从数据库中播种ID。 I want to use scope_identity() to get the id that the db generated because it is an autoincrementing column. 我想使用scope_identity()获取数据库生成的ID,因为它是一个自动递增的列。

What might be the cleanest way to achieve getting this newly inserted id? 获得此新插入的ID的最干净的方法是什么? Or, is the unit of work an inappropriate abstraction here and should be left to other use cases? 或者,这里的工作单元是不适当的抽象,应该留给其他用例吗?

In DDD the unit of work is the business process itself. 在DDD中,工作单元是业务流程本身。 What you have there is a popular anti-pattern. 您所拥有的是一种流行的反模式。 Anyway, considering your constraints the cleanest thing to do is to make the Id property assignable and set it inside the repository. 无论如何,考虑到您的约束,最干净的事情是使Id属性可分配并在存储库中进行设置。

So your project will get the id inside the repository.Add method. 因此,您的项目将在repository.Add方法中获取ID。 About adding tasks, that should be a different operation, you are trying to do too much in one operation. 关于添加任务,这应该是一项不同的操作,您正在尝试在一个操作中做太多事情。 Basically, you're using the 'old' transact script (CRUD) approach but with DDD terms. 基本上,您使用的是“旧的”事务处理脚本(CRUD)方法,但带有DDD术语。

Normally, you should have one operation for add project -> ProjectCreated[event] -> AddDefaultTasks. 通常,您应该对添加项目执行一个操作-> ProjectCreated [event]-> AddDefaultTasks。

The UI also should be more task based instead of doing 2 different things at once. 用户界面还应该更多地基于任务,而不是一次执行两种不同的操作。

I'd say, you can't really do proper DDD when the db is in charge of generating ids. 我想说,当数据库负责生成ID时,您实际上不能做适当的DDD。 The point is, DDD is not patching crappy codebase with a magic recipe, it's about doing proper design top-down. 关键是,DDD并不是用魔术方法修补糟糕的代码库,而是要自上而下地进行适当的设计。 But you are in charge of patching now, so you can't actually do DDD. 但是您现在负责打补丁,因此您实际上无法执行DDD。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM