public async Task <ActionResult> Create(CreateSampleDataViewModel model) { if (!ModelState.IsValid) { return(View(model)); } int changes; using (var context = new SampleDataDbContext()) { var sampleData = new SampleData { Name = model.Name }; context.Set <SampleData>().Add(sampleData); changes = await context.SaveChangesAsync(); } AddAlert(new SuccessAlert("Sample added", $"{changes} sample was saved to the database")); return(RedirectToAction("Index")); }
public async Task <ActionResult> ConcurrentDbTest() { // Spans should overlap (actually alternatively strictly "contain" the corresponding span on the other concurrent branch) // [---------------- INSERT A.0 ------------------------] ... [-- INSERT A.N.before --] [-- INSERT A.N.after --] // [-- INSERT B.0.before --] [-- INSERT B.0.after --] ... [---------------- INSERT B.N -----------------------] const int numberOfConcurrentIterations = ConcurrentDbTestNumberOfIterations; // Create table before concurrent inserts using (var dbCtx = new SampleDataDbContext()) { if (dbCtx.Set <SampleData>().Count() != 0) { throw new InvalidOperationException($"dbCtx.Set<SampleData>().Count(): {dbCtx.Set<SampleData>().Count()}"); } } var syncBarrier = new Barrier(2); DbInterception.Add(new ConcurrentDbTestDbCommandInterceptor(syncBarrier)); var currentTx = Agent.Tracer.CurrentTransaction; await Task.WhenAll(Task.Run(() => ConcurrentSpan("A", currentTx)), Task.Run(() => ConcurrentSpan("B", currentTx))); using (var dbCtx = new SampleDataDbContext()) { var sampleDataList = dbCtx.Set <SampleData>().ToList(); if (sampleDataList.Count != 3 * numberOfConcurrentIterations) { throw new InvalidOperationException($"sampleDataList.Count: {sampleDataList.Count}"); } for (var i = 0; i < numberOfConcurrentIterations; ++i) { var(containingPrefix, containedPrefix) = i % 2 == 0 ? ("A", "B") : ("B", "A"); var expectedNames = new List <string> { $"{containedPrefix}.{i}.before", $"{containingPrefix}.{i}", $"{containedPrefix}.{i}.after" }; var actualNames = new List <string> { sampleDataList[3 * i].Name, sampleDataList[3 * i + 1].Name, sampleDataList[3 * i + 2].Name }; if (!actualNames.SequenceEqual(expectedNames)) { throw new InvalidOperationException( $"actualNames: {string.Join(", ", actualNames)}, expectedNames: {string.Join(", ", expectedNames)}"); } } } return(new HttpStatusCodeResult(HttpStatusCode.OK)); void ConcurrentSpan(string branchId, ITransaction tx) { tx.CaptureSpan(branchId, ConcurrentDbTestSpanType, () => { for (var i = 0; i < numberOfConcurrentIterations; ++i) { var isIndexEven = i % 2 == 0; var isContainedSpan = branchId == "B" ? isIndexEven : !isIndexEven; var numberOfInserts = isContainedSpan ? 2 : 1; for (var j = 0; j < numberOfInserts; ++j) { if (isContainedSpan) { syncBarrier.SignalAndWait(); } using (var dbCtx = new SampleDataDbContext(/* attachedState: */ isContainedSpan)) { var suffix = isContainedSpan ? j == 0 ? ".before" : ".after" : ""; dbCtx.Set <SampleData>().Add(new SampleData { Name = $"{branchId}.{i}{suffix}" }); dbCtx.SaveChanges(); } if (isContainedSpan) { syncBarrier.SignalAndWait(); } } } }); } }