我即將結束一個項目,我試圖使用DDD,但發現了一個明顯的錯誤,我不確定如何輕鬆解決。重構實體方法以避免併發性問題
這裏是我的實體 - 我已經減少了它的簡單:在SetUploadResult方法發生
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,因此測試if (this.Parts.All(p => p.IsUploaded)
永遠不會評估爲true。
我不確定如何輕鬆解決此問題。將多個UploadContributionPartCommands添加到Commands集合的想法是,每個ContributionPart都可以並行上傳 - 我的CommandBus確保這一點 - 但是每個部分並行上傳,這會對我的實體邏輯造成問題。
那麼您是說有多個線程在貢獻實體的同一個實例上運行? – mm8
正確。貢獻實體爲每個partLength創建了UploadContributionPartCommand,並且每個UploadContributionPartCommandHandler並行執行,因此並行調用SetUploadResult。除了它與實體的內存實例不同,但它是同一個實體。 –
由於每個實例都有自己的部分,它們怎麼會與「內存中的實例」不一樣? – mm8