一旦一个实体被创建,它将被用例更新/操作,直到它从系统中删除。可以有不同类型的用例直接或间接地更改实体
在本节中,我们将讨论更改 Issue 的多个属性的典型更新操作。
这次,从更新DTO开始:
public class UpdateIssueDto
{
[Required]
public string Title { get; set; }
public string Text { get; set; }
public Guid? AssignedUserId { get; set; }
}
通过与 IssueCreationDto
比较,您看不到 RepositoryId
。因为,我们的系统不允许跨仓库移动问题(想想GitHub仓库)。只有 Title
是必需的,其他属性是可选的
让我们看看 IssueAppService
中的 Update
实现:
public class IssueAppService : ApplicationService, IIssueAppService
{
//省略了依赖注入
public async Task<IssueDto> UpdateAsync(Guid id, UpdateIssueDto input)
{
//从数据库获取问题实体
var issue = await _issueRepository.GetAsync(id);
//修改标题
await _issueManager.ChangeTitleAsync(issue, input.Title);
//修改分配人
if(input.AssignedUserId.HasValue)
{
var user = await _userRepository.GetAsync(input.AssignedUserId.Value);
await _issueManager.AssignToAsync(issue, user);
}
//修改内容 (没有业务规则,接受任何内容)
issue.Text = input.Text;
// 更新实体到数据库
await _issueRepository.UpdateAsync(issue);
//返回表示这个新的问题的DTO
return ObjectMapper.Map<Issue, IssueDto>(issue);
}
}
-
UpdateAsync
方法把 id 作为一个单独的参数。它不包含在UpdateIssueDto 中。这是一个设计决策,可以帮助ABP将此服务 自动暴露 为 HTTP API 端点路由。所以,这和DDD无关 -
首先从数据库中获取 Issue 实体。
-
使用
IssueManager
的ChangeTitleAsync
,而不是直接调用Issue.SetTitle(…)
因为我们需要实现重复的Title
检查,就像刚才在实体创建中所做的那样。这需要对 Issue 和 IssueManager 类进行一些更改(将在下面解释)。 -
使用
IssueManager
的AssignToAsync
方法,如果分配的用户在这个请求中被更改 -
直接设置
Issue.Text
,因为没有相关的业务规则。如果以后需要,我们总是可以进行重构 -
将更改保存到数据库。同样,保存实体是协调业务对象和事务的应用程序服务的职责。如果
IssueManager
在ChangeTitleAsync
和AssignToAsync
方法内部保存,将会有双数据库操作 参见讨论:为什么问题不在IssueManager中保存到数据库? -
最后使用
IObjectMapper
返回一个IssueDto
,该IssueDto
是通过映射从更新的 Issue 实体自动创建的
如前所述,我们需要对 Issue
和 IssueManager
类进行一些更改。
首先,在 Issue 类中设置 SetTitle 为 internal:
internal void SetTitle(string title)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title));
}
然后在 IssueManager
中添加了一个新方法来更改标题:
public async Task ChangeTitleAsync(Issue issue, string title)
{
if(issue.Title == title)
{
return;
}
//如果存在相同标题的问题,直接抛错
if(await _issueRepository.AnyAsync(i => i.Title == title))
{
throw new BusinessException("IssueTracking:IssueWithSameTitleExists");
}
issue.SetTitle(title);
}