【发布时间】:2017-02-12 16:38:12
【问题描述】:
我正在尝试使用 DDD 的项目接近尾声,但发现了一个明显的错误,我不确定如何轻松解决。
这是我的实体 - 为了简单起见,我将其简化:
public class Contribution : Entity
{
protected Contribution()
{
this.Parts = new List<ContributionPart>();
}
internal Contribution(Guid id)
{
this.Id = id;
this.Parts = new List<ContributionPart>();
}
public Guid Id { get; private set; }
protected virtual IList<ContributionPart> Parts { get; private set; }
public void UploadParts(string path, IEnumerable<long> partLengths)
{
if (this.Parts.Count > 0)
{
throw new InvalidOperationException("Parts have already been uploaded.");
}
long startPosition = 0;
int partNumber = 1;
foreach (long partLength in partLengths)
{
this.Parts.Add(new ContributionPart(this.Id, partNumber, partLength));
this.Commands.Add(new UploadContributionPartCommand(this.Id, partNumber, path, startPosition, partLength));
startPosition += partLength;
partNumber++;
}
}
public void SetUploadResult(int partNumber, string etag)
{
if (etag == null)
{
throw new ArgumentNullException(nameof(etag));
}
ContributionPart part = this.Parts.SingleOrDefault(p => p.PartNumber == partNumber);
if (part == null)
{
throw new ContributionPartNotFoundException(this.Id, partNumber);
}
part.SetUploadResult(etag);
if (this.Parts.All(p => p.IsUploaded))
{
IEnumerable<PartUploadedResult> results = this.Parts.Select(p => new PartUploadedResult(p.PartNumber, p.ETag));
this.Events.Add(new ContributionUploaded(this.Id, results));
}
}
}
我的错误发生在 SetUploadResult 方法中。基本上,多个线程同时执行上传,然后在上传结束时调用 SetUploadResult。但由于实体是在几秒钟前加载的,每个线程都会在实体的不同实例上调用 SetUploadResult,因此测试 if (this.Parts.All(p => p.IsUploaded) 永远不会评估为 true。
我不确定如何轻松解决此问题。将多个 UploadContributionPartCommands 添加到 Commands 集合背后的想法是,每个 ContributionPart 可以并行上传 - 我的 CommandBus 确保了这一点 - 但每个部分并行上传,它会导致我的实体逻辑出现问题。
【问题讨论】:
-
所以你是说有多个线程在同一个 Contribution 实体实例上运行?
-
正确。贡献实体为每个 partLength 创建了一个 UploadContributionPartCommand,每个 UploadContributionPartCommandHandler 并行执行,因此并行调用 SetUploadResult。除了它不是实体的同一个内存实例,而是同一个实体。
-
每个实例都有自己的部分,为什么不是同一个“内存中的实例”?
-
如果每个线程都在自己的实例上运行,那么您的问题在哪里?可以被多个不同线程访问的共享字段/属性是什么?
-
实体应该处理像上传数据这样的横切关注点听起来不像 DDD。我认为你的问题的根源是你试图让对象在不应该由它们负责的并行动作中合作。
标签: c# multithreading entity-framework concurrency domain-driven-design