1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Concurrent;
6 using System.Diagnostics;
7 using System.Diagnostics.Tracing;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using Xunit;
11 using Xunit.Abstractions;
12 
13 namespace System.Transactions.Tests
14 {
15     public class AsyncTransactionScopeTests
16     {
17         private readonly ITestOutputHelper output;
18 
19         // Number of threads to create
20         private const int iterations = 5;
21 
22         // The work queue that requests will be placed in to be serviced by the background thread
23         private static BlockingCollection<Tuple<int, TaskCompletionSource<object>, Transaction>> s_workQueue = new BlockingCollection<Tuple<int, TaskCompletionSource<object>, Transaction>>();
24 
25         private static bool s_throwExceptionDefaultOrBeforeAwait;
26         private static bool s_throwExceptionAfterAwait;
27 
AsyncTransactionScopeTests(ITestOutputHelper output)28         public AsyncTransactionScopeTests(ITestOutputHelper output)
29         {
30             this.output = output;
31         }
32 
33         /// <summary>
34         /// This test case will verify various Async TransactionScope usage with task and async/await and also nested mixed mode(legacy TS and async TS) usage.
35         /// </summary>
36         [Theory]
37         [InlineData(0)]
38         [InlineData(1)]
39         [InlineData(2)]
40         [InlineData(3)]
41         [InlineData(4)]
42         [InlineData(5)]
43         [InlineData(6)]
44         [InlineData(7)]
45         [InlineData(8)]
46         [InlineData(9)]
47         [InlineData(10)]
48         [InlineData(11)]
49         [InlineData(12)]
50         [InlineData(13)]
51         [InlineData(14)]
52         [InlineData(15)]
53         [InlineData(16)]
54         [InlineData(17)]
55         [InlineData(18)]
56         [InlineData(19)]
57         [InlineData(20)]
58         [InlineData(21)]
59         [InlineData(22)]
60         [InlineData(23)]
61         [InlineData(24)]
62         [InlineData(25)]
63         [InlineData(26)]
64         [InlineData(27)]
65         [InlineData(28)]
66         [InlineData(29)]
67         [InlineData(30)]
68         [InlineData(31)]
69         [InlineData(32)]
70         [InlineData(33)]
71         [InlineData(34)]
72         [InlineData(35)]
73         [InlineData(36)]
74         [InlineData(37)]
75         [InlineData(38)]
76         [InlineData(39)]
77         [InlineData(40)]
78         [InlineData(41)]
79         [InlineData(42)]
80         [InlineData(43)]
81         [InlineData(44)]
82         [InlineData(45)]
83         [InlineData(46)]
84         [InlineData(47)]
85         [InlineData(48)]
86         [InlineData(49)]
87         [InlineData(50)]
88         [InlineData(51)]
89         [InlineData(52)]
90         [InlineData(53)]
91         [InlineData(54)]
92 
AsyncTSTest(int variation)93         public void AsyncTSTest(int variation)
94         {
95             using (var listener = new TestEventListener(new Guid("8ac2d80a-1f1a-431b-ace4-bff8824aef0b"), System.Diagnostics.Tracing.EventLevel.Verbose))
96             {
97                 var events = new ConcurrentQueue<EventWrittenEventArgs>();
98 
99                 bool success = false;
100                 try
101                 {
102                     listener.RunWithCallback(events.Enqueue, () =>
103                     {
104                         switch (variation)
105                         {
106                             // Running exception test first to make sure any unintentional leak in ambient transaction during exception will be detected when subsequent test are run.
107                             case 0:
108                                 {
109                                     HandleException(true, false, () => DoSyncTxWork(true, null));
110                                     break;
111                                 }
112                             case 1:
113                                 {
114                                     HandleException(true, false, () => AssertTransactionNullAndWaitTask(DoAsyncTxWorkAsync(true, null)));
115                                     break;
116                                 }
117                             case 2:
118                                 {
119                                     HandleException(true, false, () => SyncTSL2NestedTxWork(false, false, true, null));
120                                     break;
121                                 }
122                             case 3:
123                                 {
124                                     HandleException(false, true, () => AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, true, false, null)));
125                                     break;
126                                 }
127                             case 4:
128                                 {
129                                     HandleException(true, false, () => AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, true, false, false, null)));
130                                     break;
131                                 }
132                             case 5:
133                                 {
134                                     HandleException(true, false, () => AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, false, null)));
135                                     break;
136                                 }
137                             case 6:
138                                 {
139                                     HandleException(false, true, () => AssertTransactionNullAndWaitTask(DoTaskWorkAsync(false, false, null)));
140                                     break;
141                                 }
142                             case 7:
143                                 {
144                                     HandleException(false, true, () => SyncTSTaskWork(false, null));
145                                     break;
146                                 }
147 
148                             // The following test has Task under TransactionScope and has few variations.
149                             case 8:
150                                 {
151                                     DoTaskUnderAsyncTS(false, null);
152                                     break;
153                                 }
154                             case 9:
155                                 {
156                                     Task.Factory.StartNew(() => DoTaskUnderAsyncTS(false, null)).Wait();
157                                     break;
158                                 }
159                             case 10:
160                                 {
161                                     SyncTSDoTaskUnderAsyncTS(false, false, null);
162                                     break;
163                                 }
164                             case 11:
165                                 {
166                                     SyncTSDoTaskUnderAsyncTS(false, true, null);
167                                     break;
168                                 }
169                             case 12:
170                                 {
171                                     Task.Factory.StartNew(() => SyncTSDoTaskUnderAsyncTS(false, true, null)).Wait();
172                                     break;
173                                 }
174 
175                             // Simple Sync TS test
176                             case 13:
177                                 {
178                                     DoSyncTxWork(false, null);
179                                     break;
180                                 }
181                             case 14:
182                                 {
183                                     DoSyncTxWork(true, null);
184                                     break;
185                                 }
186 
187                             // Simple Async TS test. "await" points explicitly switches threads across continuations.
188                             case 15:
189                                 {
190                                     AssertTransactionNullAndWaitTask(DoAsyncTxWorkAsync(false, null));
191                                     break;
192                                 }
193                             case 16:
194                                 {
195                                     AssertTransactionNullAndWaitTask(DoAsyncTxWorkAsync(true, null));
196                                     break;
197                                 }
198 
199                             // Nested TS test. Parent is Sync TS, child TS can be sync or async.
200                             case 17:
201                                 {
202                                     SyncTSL2NestedTxWork(false, false, false, null);
203                                     break;
204                                 }
205                             case 18:
206                                 {
207                                     SyncTSL2NestedTxWork(false, false, true, null);
208                                     break;
209                                 }
210                             case 19:
211                                 {
212                                     SyncTSL2NestedTxWork(false, true, false, null);
213                                     break;
214                                 }
215                             case 20:
216                                 {
217                                     SyncTSL2NestedTxWork(false, true, true, null);
218                                     break;
219                                 }
220                             case 21:
221                                 {
222                                     SyncTSL2NestedTxWork(true, false, false, null);
223                                     break;
224                                 }
225                             case 22:
226                                 {
227                                     SyncTSL2NestedTxWork(true, false, true, null);
228                                     break;
229                                 }
230                             case 23:
231                                 {
232                                     SyncTSL2NestedTxWork(true, true, false, null);
233                                     break;
234                                 }
235                             case 24:
236                                 {
237                                     SyncTSL2NestedTxWork(true, true, true, null);
238                                     break;
239                                 }
240 
241                             // 2 level deep nested TS test. Parent is Aync TS, child TS can be sync or async.
242                             case 25:
243                                 {
244                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, false, false, false, false, null));
245                                     break;
246                                 }
247                             case 26:
248                                 {
249                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, false, true, false, false, null));
250                                     break;
251                                 }
252                             case 27:
253                                 {
254                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, false, null));
255                                     break;
256                                 }
257                             case 28:
258                                 {
259                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, true, false, false, null));
260                                     break;
261                                 }
262                             case 29:
263                                 {
264                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, false, false, false, false, null));
265                                     break;
266                                 }
267                             case 30:
268                                 {
269                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, false, true, false, false, null));
270                                     break;
271                                 }
272                             case 31:
273                                 {
274                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, true, false, false, false, null));
275                                     break;
276                                 }
277                             case 32:
278                                 {
279                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(true, true, true, false, false, null));
280                                     break;
281                                 }
282 
283                             // Introduce various "await" points to switch threads before/after child TransactionScope.
284                             // Introduce some Task variations by running the test under Task.
285                             case 33:
286                                 {
287                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, true, false, null));
288                                     break;
289                                 }
290                             case 34:
291                                 {
292                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, true, null));
293                                     break;
294                                 }
295                             case 35:
296                                 {
297                                     Task.Factory.StartNew(() => AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, true, null))).Wait();
298                                     break;
299                                 }
300                             case 36:
301                                 {
302                                     AssertTransactionNullAndWaitTask(DoAsyncTSL2NestedTxWorkAsync(false, true, false, true, true, null));
303                                     break;
304                                 }
305 
306                             // 3 level deep nested TS test.
307                             case 37:
308                                 {
309                                     SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, false, true, null);
310                                     break;
311                                 }
312                             case 38:
313                                 {
314                                     Task.Factory.StartNew(() => SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, false, true, null)).Wait();
315                                     break;
316                                 }
317                             case 39:
318                                 {
319                                     SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, true, false, null);
320                                     break;
321                                 }
322 
323                             case 40:
324                                 {
325                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, false, true, false, false, null));
326                                     break;
327                                 }
328                             case 41:
329                                 {
330                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, false, true, null));
331                                     break;
332                                 }
333                             case 42:
334                                 {
335                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, true, false, null));
336                                     break;
337                                 }
338                             case 43:
339                                 {
340                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, true, true, null));
341                                     break;
342                                 }
343                             case 44:
344                                 {
345                                     Task.Factory.StartNew(() => AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, false, true, false, true, true, null))).Wait();
346                                     break;
347                                 }
348 
349                             case 45:
350                                 {
351                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, false, true, null));
352                                     break;
353                                 }
354                             case 46:
355                                 {
356                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, false, null));
357                                     break;
358                                 }
359                             case 47:
360                                 {
361                                     AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, true, null));
362                                     break;
363                                 }
364                             case 48:
365                                 {
366                                     Task.Factory.StartNew(() => AssertTransactionNullAndWaitTask(DoAsyncTSL3SyncTSL2NestedTxWorkAsync(false, true, true, false, true, true, null))).Wait();
367                                     break;
368                                 }
369 
370                             // Have bunch of parallel tasks running various nested TS test cases. There parallel tasks are wrapped by a TransactionScope.
371                             case 49:
372                                 {
373                                     AssertTransactionNullAndWaitTask(DoTaskWorkAsync(false, false, null));
374                                     break;
375                                 }
376                             case 50:
377                                 {
378                                     SyncTSTaskWork(false, null);
379                                     break;
380                                 }
381                             case 51:
382                                 {
383                                     SyncTSTaskWork(true, null);
384                                     break;
385                                 }
386                             case 52:
387                                 {
388                                     AssertTransactionNullAndWaitTask(DoAsyncTSTaskWorkAsync(false, false, null));
389                                     break;
390                                 }
391                             case 53:
392                                 {
393                                     AssertTransactionNullAndWaitTask(DoAsyncTSTaskWorkAsync(true, false, null));
394                                     break;
395                                 }
396 
397                             // Final test - wrap the DoAsyncTSTaskWorkAsync in syncronous scope
398                             case 54:
399                                 {
400                                     string txId1 = null;
401                                     string txId2 = null;
402 
403                                     using (TransactionScope scope = new TransactionScope())
404                                     {
405                                         txId1 = AssertAndGetCurrentTransactionId();
406                                         Task task = DoAsyncTSTaskWorkAsync(false, false, txId1);
407                                         txId2 = AssertAndGetCurrentTransactionId();
408                                         task.Wait();
409                                         scope.Complete();
410                                     }
411 
412                                     VerifyTxId(false, null, txId1, txId2);
413                                     break;
414                                 }
415                         }
416                     });
417                     success = true;
418                 }
419                 finally
420                 {
421                     if (!success)
422                     {
423                         HelperFunctions.DisplaySysTxTracing(output, events);
424                     }
425                 }
426             }
427         }
428 
429         [Theory]
430         [InlineData(true, false, null)]
431         [InlineData(true, true, null)]
AsyncTSAndDependantClone(bool requiresNew, bool syncronizeScope, string txId)432         public void AsyncTSAndDependantClone(bool requiresNew, bool syncronizeScope, string txId)
433         {
434             string txId1 = null;
435             string txId2 = null;
436             string txId3 = null;
437             string txId4 = null;
438             string txId5 = null;
439             string txId6 = null;
440             string txId7 = null;
441             Task task2;
442 
443             AssertTransaction(txId);
444 
445             using (TransactionScope scope1 = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
446             {
447                 txId1 = AssertAndGetCurrentTransactionId();
448                 DependentTransaction dependentTx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
449                 Task task1 = Task.Run(delegate
450                 {
451                     try
452                     {
453                         // Since we use BlockCommitUntilComplete dependent transaction to syncronize the root TransactionScope, the ambient Tx may not be available and will be be disposed and block on Commit.
454                         // The flag will ensure we explicitly syncronize before disposing the root TransactionScope and the ambient transaction will still be available in the Task.
455                         if (syncronizeScope)
456                         {
457                             txId2 = AssertAndGetCurrentTransactionId();
458                         }
459 
460                         using (TransactionScope scope2 = new TransactionScope(dependentTx))
461                         {
462                             txId3 = AssertAndGetCurrentTransactionId();
463                             Task.Delay(10).Wait();
464                             scope2.Complete();
465                         }
466 
467                         if (syncronizeScope)
468                         {
469                             txId4 = AssertAndGetCurrentTransactionId();
470                         }
471                     }
472                     finally
473                     {
474                         dependentTx.Complete();
475                         dependentTx.Dispose();
476                     }
477 
478                     if (syncronizeScope)
479                     {
480                         txId5 = AssertAndGetCurrentTransactionId();
481                     }
482                 });
483 
484                 task2 = Task.Run(delegate
485                 {
486                     using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew))
487                     {
488                         txId7 = AssertAndGetCurrentTransactionId();
489                         scope3.Complete();
490                     }
491                 });
492 
493                 txId6 = AssertAndGetCurrentTransactionId();
494 
495                 if (syncronizeScope)
496                 {
497                     task1.Wait();
498                 }
499                 scope1.Complete();
500             }
501 
502             task2.Wait();
503 
504             VerifyTxId(requiresNew, txId, txId1, txId6);
505 
506             Assert.Equal(txId1, txId3);
507             Assert.Equal(txId3, txId6);
508             if (syncronizeScope)
509             {
510                 Assert.Equal(txId1, txId2);
511                 Assert.Equal(txId1, txId4);
512                 Assert.Equal(txId4, txId5);
513             }
514             Assert.NotEqual(txId1, txId7);
515 
516             AssertTransaction(txId);
517         }
518 
519         [Theory]
520         [InlineData(true, false, null)]
521         [InlineData(true, true, null)]
NestedAsyncTSAndDependantClone(bool parentrequiresNew, bool childRequiresNew, string txId)522         public void NestedAsyncTSAndDependantClone(bool parentrequiresNew, bool childRequiresNew, string txId)
523         {
524             string txId1;
525             string txId2;
526 
527             AssertTransaction(txId);
528 
529             using (TransactionScope scope = new TransactionScope(parentrequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
530             {
531                 txId1 = AssertAndGetCurrentTransactionId();
532                 AsyncTSAndDependantClone(childRequiresNew, true, txId1);
533                 txId2 = AssertAndGetCurrentTransactionId();
534                 scope.Complete();
535             }
536 
537             AssertTransaction(txId);
538 
539             VerifyTxId(parentrequiresNew, txId, txId1, txId2);
540         }
541 
DoAsyncTSTaskWorkAsync(bool requiresNew, bool parentAsync, string txId)542         private async Task DoAsyncTSTaskWorkAsync(bool requiresNew, bool parentAsync, string txId)
543         {
544             string txId1 = null;
545             string txId2 = null;
546             string txId3 = null;
547             string txId4 = null;
548             string txId5 = null;
549 
550             Task task1;
551             int startThreadId, endThreadId;
552             bool parentScopePresent = false;
553 
554             if (!string.IsNullOrEmpty(txId))
555             {
556                 parentScopePresent = true;
557             }
558 
559             startThreadId = Environment.CurrentManagedThreadId;
560 
561             AssertTransaction(txId);
562 
563             using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
564             {
565                 txId1 = AssertAndGetCurrentTransactionId();
566 
567                 task1 = Task.Run(delegate
568                 {
569                     txId3 = AssertAndGetCurrentTransactionId();
570                     SyncTSTaskWork(requiresNew, txId1);
571                     txId4 = AssertAndGetCurrentTransactionId();
572                 });
573 
574                 await DoTaskWorkAsync(requiresNew, true, txId1);
575                 txId5 = AssertAndGetCurrentTransactionId();
576 
577                 await task1;
578 
579                 txId2 = AssertAndGetCurrentTransactionId();
580 
581                 scope.Complete();
582             }
583 
584             VerifyTxId(requiresNew, txId, txId1, txId2);
585             Assert.Equal(txId1, txId5);
586 
587             endThreadId = Environment.CurrentManagedThreadId;
588 
589             if (parentScopePresent)
590             {
591                 if (parentAsync)
592                 {
593                     AssertTransaction(txId);
594                 }
595                 else
596                 {
597                     if (startThreadId != endThreadId)
598                     {
599                         AssertTransactionNull();
600                     }
601                     else
602                     {
603                         AssertTransaction(txId);
604                     }
605                 }
606             }
607             else
608             {
609                 AssertTransactionNull();
610             }
611         }
612 
SyncTSTaskWork(bool requiresNew, string txId)613         private void SyncTSTaskWork(bool requiresNew, string txId)
614         {
615             string txId1 = null;
616             string txId2 = null;
617 
618             using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
619             {
620                 txId1 = AssertAndGetCurrentTransactionId();
621                 Task task = DoTaskWorkAsync(requiresNew, false, txId1);
622                 txId2 = AssertAndGetCurrentTransactionId();
623                 task.Wait();
624                 scope.Complete();
625             }
626 
627             VerifyTxId(requiresNew, txId, txId1, txId2);
628         }
629 
DoTaskWorkAsync(bool requiresNew, bool parentAsync, string txId)630         private async Task DoTaskWorkAsync(bool requiresNew, bool parentAsync, string txId)
631         {
632             string txId1 = null;
633             string txId2 = null;
634             string txId3 = null;
635             string txId4 = null;
636             string txId5 = null;
637             string txId6 = null;
638             string txId7 = null;
639             string txId8 = null;
640             string txId9 = null;
641             string txId10 = null;
642             string txId11 = null;
643             string txId12 = null;
644             string txId13 = null;
645             string txId14 = null;
646             string txId15 = null;
647 
648             Task task1, task2, task3, task4, task5, task6;
649             int startThreadId, endThreadId;
650             bool parentScopePresent = false;
651 
652             if (!string.IsNullOrEmpty(txId))
653             {
654                 parentScopePresent = true;
655             }
656 
657             startThreadId = Environment.CurrentManagedThreadId;
658 
659             AssertTransaction(txId);
660 
661             using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
662             {
663                 txId1 = AssertAndGetCurrentTransactionId();
664 
665                 task1 = Task.Run(async delegate
666                 {
667                     txId3 = AssertAndGetCurrentTransactionId();
668                     await DoAsyncTSL2NestedTxWorkAsync(false, false, true, false, false, txId1);
669                     txId4 = AssertAndGetCurrentTransactionId();
670                 });
671 
672                 task2 = Task.Run(async delegate
673                 {
674                     txId5 = AssertAndGetCurrentTransactionId();
675                     await DoAsyncTSL2NestedTxWorkAsync(true, false, true, false, false, txId5);
676 
677                     txId6 = AssertAndGetCurrentTransactionId();
678                 });
679 
680                 task3 = Task.Run(delegate
681                 {
682                     txId7 = AssertAndGetCurrentTransactionId();
683                     NestedAsyncTSAndDependantClone(false, true, txId7);
684                     txId8 = AssertAndGetCurrentTransactionId();
685                 });
686 
687                 task4 = Task.Run(delegate
688                 {
689                     txId9 = AssertAndGetCurrentTransactionId();
690                     SyncTSL2NestedTxWork(false, false, true, txId6);
691                     txId10 = AssertAndGetCurrentTransactionId();
692 
693                     Task.Run(delegate
694                     {
695                         txId15 = AssertAndGetCurrentTransactionId();
696                     }).Wait();
697                 });
698 
699                 task5 = Task.Run(async delegate
700                 {
701                     txId11 = AssertAndGetCurrentTransactionId();
702                     await DoAsyncTSL2NestedTxWorkAsync(false, true, false, false, true, txId11);
703                     txId12 = AssertAndGetCurrentTransactionId();
704                 });
705 
706                 task6 = Task.Run(delegate
707                 {
708                     txId13 = AssertAndGetCurrentTransactionId();
709                     SyncTSL3AsyncTSL2NestedTxWork(false, false, true, false, false, true, txId13);
710                     txId14 = AssertAndGetCurrentTransactionId();
711                 });
712 
713                 await DoAsyncTSL2NestedTxWorkAsync(false, false, true, false, false, txId1);
714 
715                 txId2 = AssertAndGetCurrentTransactionId();
716 
717                 Task.WaitAll(task1, task2, task3, task4, task5, task6);
718                 scope.Complete();
719             }
720 
721             VerifyTxId(requiresNew, txId, txId1, txId2);
722             VerifyTxId(false, txId1, txId3, txId4);
723             VerifyTxId(false, txId1, txId5, txId6);
724             VerifyTxId(false, txId1, txId7, txId8);
725             VerifyTxId(false, txId1, txId9, txId10);
726             VerifyTxId(false, txId1, txId11, txId12);
727             VerifyTxId(false, txId1, txId13, txId14);
728 
729             Assert.Equal(txId1, txId15);
730             endThreadId = Environment.CurrentManagedThreadId;
731 
732             if (parentScopePresent)
733             {
734                 if (parentAsync)
735                 {
736                     AssertTransaction(txId);
737                 }
738                 else
739                 {
740                     if (startThreadId != endThreadId)
741                     {
742                         AssertTransactionNull();
743                     }
744                     else
745                     {
746                         AssertTransaction(txId);
747                     }
748                 }
749             }
750             else
751             {
752                 AssertTransactionNull();
753             }
754         }
755 
756         [Fact]
LegacyNestedTxScope()757         public void LegacyNestedTxScope()
758         {
759             string txId1 = null;
760             string txId2 = null;
761             string txId3 = null;
762             string txId4 = null;
763             string txId5 = null;
764             string txId6 = null;
765 
766             Debug.WriteLine("Running NestedScopeTest");
767             AssertTransactionNull();
768 
769             // Test Sync nested TransactionScope behavior.
770             using (TransactionScope scope = new TransactionScope())
771             {
772                 txId1 = AssertAndGetCurrentTransactionId();
773                 scope.Complete();
774             }
775 
776             AssertTransactionNull();
777 
778             using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Required))
779             {
780                 txId2 = AssertAndGetCurrentTransactionId();
781 
782                 using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.Suppress))
783                 {
784                     AssertTransactionNull();
785                     scope3.Complete();
786                 }
787 
788                 txId6 = AssertAndGetCurrentTransactionId();
789 
790                 using (TransactionScope scope4 = new TransactionScope(TransactionScopeOption.RequiresNew))
791                 {
792                     txId3 = AssertAndGetCurrentTransactionId();
793                     scope4.Complete();
794                 }
795 
796                 txId4 = AssertAndGetCurrentTransactionId();
797                 scope2.Complete();
798             }
799 
800             AssertTransactionNull();
801 
802             using (TransactionScope scope = new TransactionScope())
803             {
804                 txId5 = AssertAndGetCurrentTransactionId();
805                 scope.Complete();
806             }
807 
808             AssertTransactionNull();
809 
810             Assert.Equal(txId2, txId4);
811             Assert.Equal(txId2, txId6);
812             Assert.NotEqual(txId1, txId2);
813             Assert.NotEqual(txId2, txId3);
814             Assert.NotEqual(txId1, txId5);
815         }
816 
817         [Theory]
818         // Async TS nested inside Sync TS
819         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
820         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)]
821         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)]
822         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
823         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)]
824         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)]
825         // Sync TS nested inside Async TS.
826         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)]
827         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)]
828         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)]
829         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)]
830         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)]
831         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)]
832         // Async TS nested inside Async TS
833         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
834         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)]
835         [InlineData(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)]
836         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
837         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)]
838         [InlineData(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)]
VerifyNestedTxScope(TransactionScopeOption parentScopeOption, TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption childScopeOption, TransactionScopeAsyncFlowOption childAsyncFlowOption)839         public void VerifyNestedTxScope(TransactionScopeOption parentScopeOption, TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption childScopeOption, TransactionScopeAsyncFlowOption childAsyncFlowOption)
840         {
841             string txId1;
842             string txId2;
843             string txId3;
844 
845             AssertTransactionNull();
846 
847             using (TransactionScope parent = new TransactionScope(parentScopeOption, parentAsyncFlowOption))
848             {
849                 txId1 = AssertAndGetCurrentTransactionId(parentScopeOption);
850 
851                 using (TransactionScope child = new TransactionScope(childScopeOption, childAsyncFlowOption))
852                 {
853                     txId2 = AssertAndGetCurrentTransactionId(childScopeOption);
854                     child.Complete();
855                 }
856 
857                 txId3 = AssertAndGetCurrentTransactionId(parentScopeOption);
858                 parent.Complete();
859             }
860 
861             AssertTransactionNull();
862             Assert.Equal(txId1, txId3);
863             switch (childScopeOption)
864             {
865                 case TransactionScopeOption.Required:
866                     if (parentScopeOption == TransactionScopeOption.Suppress)
867                     {
868                         Assert.NotEqual(txId1, txId2);
869                     }
870                     else
871                     {
872                         Assert.Equal(txId1, txId2);
873                     }
874                     break;
875                 case TransactionScopeOption.RequiresNew:
876                     Assert.NotEqual(txId1, txId2);
877                     break;
878                 case TransactionScopeOption.Suppress:
879                     if (parentScopeOption == TransactionScopeOption.Suppress)
880                     {
881                         Assert.Equal(txId1, txId2);
882                     }
883                     else
884                     {
885                         Assert.NotEqual(txId1, txId2);
886                     }
887                     break;
888             }
889         }
890 
891         [Theory]
892         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
893         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)]
894         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
895         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)]
896         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)]
897         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)]
898         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled)]
899         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Suppress)]
900         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
901         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)]
902         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)]
903         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress)]
904         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)]
905         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)]
906         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)]
907         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Suppress)]
VerifyParentTwoChildTest(TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption child1ScopeOption, TransactionScopeAsyncFlowOption child1AsyncFlowOption, TransactionScopeOption child2ScopeOption, TransactionScopeAsyncFlowOption child2AsyncFlowOption)908         public void VerifyParentTwoChildTest(TransactionScopeAsyncFlowOption parentAsyncFlowOption, TransactionScopeOption child1ScopeOption, TransactionScopeAsyncFlowOption child1AsyncFlowOption, TransactionScopeOption child2ScopeOption, TransactionScopeAsyncFlowOption child2AsyncFlowOption)
909         {
910             string txId1;
911             string txId2;
912             string txId3;
913             string txId4;
914             string txId5;
915 
916             AssertTransactionNull();
917 
918             using (TransactionScope parent = new TransactionScope(parentAsyncFlowOption))
919             {
920                 txId1 = AssertAndGetCurrentTransactionId();
921 
922                 using (TransactionScope child1 = new TransactionScope(child1ScopeOption, child1AsyncFlowOption))
923                 {
924                     txId2 = AssertAndGetCurrentTransactionId(child1ScopeOption);
925                     child1.Complete();
926                 }
927 
928                 txId3 = AssertAndGetCurrentTransactionId();
929 
930                 using (TransactionScope child2 = new TransactionScope(child2ScopeOption, child2AsyncFlowOption))
931                 {
932                     txId4 = AssertAndGetCurrentTransactionId(child2ScopeOption);
933                     child2.Complete();
934                 }
935 
936                 txId5 = AssertAndGetCurrentTransactionId();
937                 parent.Complete();
938             }
939 
940             AssertTransactionNull();
941             Assert.Equal(txId1, txId3);
942             Assert.Equal(txId3, txId5);
943 
944             if (child1ScopeOption == TransactionScopeOption.Required)
945             {
946                 Assert.Equal(txId1, txId2);
947             }
948             else
949             {
950                 Assert.NotEqual(txId1, txId2);
951                 if (child1ScopeOption == TransactionScopeOption.Suppress)
952                 {
953                     Assert.Equal(null, txId2);
954                 }
955             }
956 
957             if (child2ScopeOption == TransactionScopeOption.Required)
958             {
959                 Assert.Equal(txId1, txId4);
960             }
961             else
962             {
963                 Assert.NotEqual(txId1, txId4);
964                 if (child2ScopeOption == TransactionScopeOption.Suppress)
965                 {
966                     Assert.Equal(null, txId4);
967                 }
968             }
969         }
970 
971         [Theory]
972         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, false)]
973         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, false)]
974         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Required, true)]
975         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, true)]
976         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, false)]
977         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Required, true)]
978         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.RequiresNew, true)]
979         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, true)]
980         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.RequiresNew, true)]
981         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, false)]
982         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, false)]
983         [InlineData(TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeOption.Suppress, true)]
984         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Suppress, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, true)]
985         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, false)]
986         [InlineData(TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeAsyncFlowOption.Enabled, TransactionScopeOption.Suppress, true)]
VerifyThreeTxScope(TransactionScopeAsyncFlowOption asyncFlowOption1, TransactionScopeAsyncFlowOption asyncFlowOption2, TransactionScopeAsyncFlowOption asyncFlowOption3, TransactionScopeOption scopeOption, bool nested)987         public void VerifyThreeTxScope(TransactionScopeAsyncFlowOption asyncFlowOption1, TransactionScopeAsyncFlowOption asyncFlowOption2, TransactionScopeAsyncFlowOption asyncFlowOption3, TransactionScopeOption scopeOption, bool nested)
988         {
989             string txId1;
990             string txId2;
991             string txId3;
992             string txId4;
993             string txId5;
994 
995             AssertTransactionNull();
996 
997             if (nested)
998             {
999                 using (TransactionScope scope1 = new TransactionScope(scopeOption, asyncFlowOption1))
1000                 {
1001                     txId1 = AssertAndGetCurrentTransactionId(scopeOption);
1002                     using (TransactionScope scope2 = new TransactionScope(scopeOption, asyncFlowOption2))
1003                     {
1004                         txId2 = AssertAndGetCurrentTransactionId(scopeOption);
1005                         using (TransactionScope scope3 = new TransactionScope(scopeOption, asyncFlowOption3))
1006                         {
1007                             txId3 = AssertAndGetCurrentTransactionId(scopeOption);
1008                             scope3.Complete();
1009                         }
1010 
1011                         txId4 = AssertAndGetCurrentTransactionId(scopeOption);
1012                         scope2.Complete();
1013                     }
1014 
1015                     txId5 = AssertAndGetCurrentTransactionId(scopeOption);
1016                     scope1.Complete();
1017                 }
1018 
1019                 AssertTransactionNull();
1020                 Assert.Equal(txId1, txId5);
1021                 Assert.Equal(txId2, txId4);
1022                 if (scopeOption == TransactionScopeOption.RequiresNew)
1023                 {
1024                     Assert.NotEqual(txId1, txId2);
1025                     Assert.NotEqual(txId2, txId3);
1026                 }
1027                 else
1028                 {
1029                     Assert.Equal(txId1, txId2);
1030                     Assert.Equal(txId2, txId3);
1031                 }
1032             }
1033             else
1034             {
1035                 using (TransactionScope scope1 = new TransactionScope(scopeOption, asyncFlowOption1))
1036                 {
1037                     txId1 = AssertAndGetCurrentTransactionId(scopeOption);
1038                     scope1.Complete();
1039                 }
1040 
1041                 AssertTransactionNull();
1042                 using (TransactionScope scope2 = new TransactionScope(scopeOption, asyncFlowOption2))
1043                 {
1044                     txId2 = AssertAndGetCurrentTransactionId(scopeOption);
1045                     scope2.Complete();
1046                 }
1047 
1048                 AssertTransactionNull();
1049                 using (TransactionScope scope3 = new TransactionScope(scopeOption, asyncFlowOption3))
1050                 {
1051                     txId3 = AssertAndGetCurrentTransactionId(scopeOption);
1052                     scope3.Complete();
1053                 }
1054 
1055                 if (scopeOption == TransactionScopeOption.Suppress)
1056                 {
1057                     Assert.Equal(null, txId1);
1058                     Assert.Equal(txId1, txId2);
1059                     Assert.Equal(txId2, txId3);
1060                 }
1061                 else
1062                 {
1063                     Assert.NotEqual(txId1, txId2);
1064                     Assert.NotEqual(txId2, txId3);
1065                 }
1066             }
1067 
1068             AssertTransactionNull();
1069         }
1070 
1071         [Theory]
1072         [InlineData(TransactionScopeAsyncFlowOption.Suppress)]
1073         [InlineData(TransactionScopeAsyncFlowOption.Enabled)]
VerifyBYOT(TransactionScopeAsyncFlowOption asyncFlowOption)1074         public void VerifyBYOT(TransactionScopeAsyncFlowOption asyncFlowOption)
1075         {
1076             string txId1;
1077             string txId2;
1078             string txId3;
1079 
1080             AssertTransactionNull();
1081             CommittableTransaction tx = new CommittableTransaction();
1082             Transaction.Current = tx;
1083 
1084             txId1 = AssertAndGetCurrentTransactionId();
1085             using (TransactionScope scope = new TransactionScope(asyncFlowOption))
1086             {
1087                 txId2 = AssertAndGetCurrentTransactionId();
1088                 scope.Complete();
1089             }
1090 
1091             txId3 = AssertAndGetCurrentTransactionId();
1092             Transaction.Current = null;
1093             tx.Commit();
1094 
1095             Assert.Equal(txId1, txId2);
1096             Assert.Equal(txId2, txId3);
1097 
1098             AssertTransactionNull();
1099         }
1100 
1101         [Fact]
VerifyBYOTOpenConnSimulationTest()1102         public void VerifyBYOTOpenConnSimulationTest()
1103         {
1104             // Create threads to do work
1105             Task[] threads = new Task[iterations];
1106             for (int i = 0; i < iterations; i++)
1107             {
1108                 threads[i] = Task.Factory.StartNew((object o) => { SimulateOpenConnTestAsync((int)o).Wait(); }, i, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
1109             }
1110 
1111             for (int i = 0; i < threads.Length; i++)
1112             {
1113                 threads[i].Wait();
1114             }
1115         }
1116 
1117         [Fact]
VerifyBYOTSyncTSNestedAsync()1118         public void VerifyBYOTSyncTSNestedAsync()
1119         {
1120             string txId1;
1121             string txId2;
1122 
1123             AssertTransactionNull();
1124 
1125             CommittableTransaction tx = new CommittableTransaction();
1126             Transaction.Current = tx;
1127 
1128             txId1 = AssertAndGetCurrentTransactionId();
1129             SyncTSL3AsyncTSL2NestedTxWork(false, false, false, true, false, false, txId1);
1130             txId2 = AssertAndGetCurrentTransactionId();
1131 
1132             Transaction.Current = null;
1133             tx.Commit();
1134 
1135             Assert.Equal(txId1, txId2);
1136 
1137             AssertTransactionNull();
1138         }
1139 
1140         [Fact]
VerifyBYOTAsyncTSNestedAsync()1141         public void VerifyBYOTAsyncTSNestedAsync()
1142         {
1143             string txId1;
1144             string txId2;
1145 
1146             AssertTransactionNull();
1147             CommittableTransaction tx = new CommittableTransaction();
1148             Transaction.Current = tx;
1149 
1150             txId1 = AssertAndGetCurrentTransactionId();
1151             Task<string> task = DoAsyncTSL3AsyncTSL2NestedTxWorkAsync(false, false, false, true, false, false, txId1);
1152             txId2 = AssertAndGetCurrentTransactionId();
1153 
1154             string result = task.Result;
1155 
1156             Transaction.Current = null;
1157             tx.Commit();
1158 
1159             Assert.Equal(txId1, txId2);
1160 
1161             AssertTransactionNull();
1162         }
1163 
1164         [Theory]
1165         [InlineData(TransactionScopeAsyncFlowOption.Suppress)]
1166         [InlineData(TransactionScopeAsyncFlowOption.Enabled)]
DoTxQueueWorkItem(TransactionScopeAsyncFlowOption asyncFlowOption)1167         public void DoTxQueueWorkItem(TransactionScopeAsyncFlowOption asyncFlowOption)
1168         {
1169             string txId1;
1170             string txId2;
1171 
1172             ManualResetEvent waitCompletion = new ManualResetEvent(false);
1173             AssertTransactionNull();
1174 
1175             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, asyncFlowOption))
1176             {
1177                 txId1 = AssertAndGetCurrentTransactionId();
1178 
1179                 ThreadSyncObject context = new ThreadSyncObject()
1180                 {
1181                     Id = txId1,
1182                     Event = waitCompletion,
1183                     RootAsyncFlowOption = asyncFlowOption
1184                 };
1185 
1186                 Task.Factory.StartNew(DoTxWork, context, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
1187 
1188                 waitCompletion.WaitOne();
1189 
1190                 txId2 = AssertAndGetCurrentTransactionId();
1191 
1192                 scope.Complete();
1193             }
1194 
1195             Assert.Equal(txId1, txId2);
1196 
1197             AssertTransactionNull();
1198         }
1199 
1200         [Theory]
1201         [InlineData(TransactionScopeAsyncFlowOption.Suppress)]
1202         [InlineData(TransactionScopeAsyncFlowOption.Enabled)]
DoTxNewThread(TransactionScopeAsyncFlowOption asyncFlowOption)1203         public void DoTxNewThread(TransactionScopeAsyncFlowOption asyncFlowOption)
1204         {
1205             string txId1;
1206             string txId2;
1207 
1208             ManualResetEvent waitCompletion = new ManualResetEvent(false);
1209             AssertTransactionNull();
1210 
1211             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, asyncFlowOption))
1212             {
1213                 txId1 = AssertAndGetCurrentTransactionId();
1214 
1215                 ThreadSyncObject context = new ThreadSyncObject()
1216                 {
1217                     Id = txId1,
1218                     RootAsyncFlowOption = asyncFlowOption
1219                 };
1220 
1221                 Task.Factory.StartNew(DoTxWork, context, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).Wait();
1222 
1223                 txId2 = AssertAndGetCurrentTransactionId();
1224 
1225                 scope.Complete();
1226             }
1227 
1228             AssertTransactionNull();
1229             Assert.Equal(txId1, txId2);
1230         }
1231 
SimulateOpenConnTestAsync(int i)1232         private static async Task SimulateOpenConnTestAsync(int i)
1233         {
1234             string txId1;
1235             string txId2;
1236 
1237             TaskCompletionSource<object> completionSource = new TaskCompletionSource<object>();
1238 
1239             // Start a transaction - presumably the customer would do this in our code
1240             using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, TransactionScopeAsyncFlowOption.Enabled))
1241             {
1242                 txId1 = AssertAndGetCurrentTransactionId();
1243                 // At some point we realize that we can't complete our work, so enqueue it to be completed later
1244                 s_workQueue.Add(Tuple.Create(i, completionSource, Transaction.Current));
1245 
1246                 // If we are the first thread kicked off, then we are also resposible to kick of the background thread which will do the work for us
1247                 if (i == 0)
1248                 {
1249                     StartWorkProcessingThread();
1250                 }
1251 
1252                 // Await for our work to be completed by the background thread, then finish the Tx
1253                 await completionSource.Task;
1254 
1255                 txId2 = AssertAndGetCurrentTransactionId();
1256                 ts.Complete();
1257             }
1258 
1259             Assert.Equal(txId1, txId2);
1260         }
1261 
StartWorkProcessingThread()1262         private static void StartWorkProcessingThread()
1263         {
1264             Task.Factory.StartNew(() =>
1265             {
1266                 for (int j = 0; j < iterations; j++)
1267                 {
1268                     //Get the next item of work
1269                     var work = s_workQueue.Take();
1270 
1271                     // Set the current transaction, such that anything we call will be aware of it
1272                     Transaction.Current = work.Item3;
1273 
1274                     // Read the current transaction back and check to see if it is what we set
1275                     Assert.Equal(Transaction.Current, work.Item3);
1276 
1277                     Debug.WriteLine("{0}: {1}", work.Item1, Transaction.Current == work.Item3);
1278 
1279                     // Tell the other thread that we completed its work
1280                     work.Item2.SetResult(null);
1281                 }
1282             }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
1283         }
1284 
DoTxWork(object obj)1285         private static void DoTxWork(object obj)
1286         {
1287             string txId1 = null;
1288             string txId2 = null;
1289 
1290             ThreadSyncObject context = (ThreadSyncObject)obj;
1291 
1292             if (context.RootAsyncFlowOption == TransactionScopeAsyncFlowOption.Enabled)
1293             {
1294                 txId1 = AssertAndGetCurrentTransactionId();
1295                 Assert.Equal(context.Id, txId1);
1296             }
1297             else
1298             {
1299                 AssertTransactionNull();
1300             }
1301 
1302             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
1303             {
1304                 txId2 = AssertAndGetCurrentTransactionId();
1305                 scope.Complete();
1306             }
1307 
1308             if (context.RootAsyncFlowOption == TransactionScopeAsyncFlowOption.Enabled)
1309             {
1310                 string txId3 = AssertAndGetCurrentTransactionId();
1311                 Assert.Equal(txId1, txId2);
1312                 Assert.Equal(txId1, txId3);
1313             }
1314             else
1315             {
1316                 AssertTransactionNull();
1317             }
1318 
1319             if (context.Event != null)
1320             {
1321                 context.Event.Set();
1322             }
1323         }
1324 
DoSyncTxWork(bool requiresNew, string txId)1325         private static string DoSyncTxWork(bool requiresNew, string txId)
1326         {
1327             string txId1 = null;
1328             string txId2 = null;
1329             string result;
1330             using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
1331             {
1332                 txId1 = AssertAndGetCurrentTransactionId();
1333                 result = DoWork(txId1);
1334                 txId2 = AssertAndGetCurrentTransactionId();
1335 
1336                 scope.Complete();
1337             }
1338 
1339             VerifyTxId(requiresNew, txId, txId1, txId2);
1340 
1341             return result;
1342         }
1343 
DoWork(string txId)1344         private static string DoWork(string txId)
1345         {
1346             string txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null;
1347             Assert.Equal(txId, txId1);
1348 
1349             if (s_throwExceptionDefaultOrBeforeAwait)
1350             {
1351                 throw new Exception("Sync DoWork exception!");
1352             }
1353 
1354             return "Hello" + " World";
1355         }
1356 
DoAsyncTxWorkAsync(bool requiresNew, string txId)1357         private static async Task<string> DoAsyncTxWorkAsync(bool requiresNew, string txId)
1358         {
1359             string txId1 = null;
1360             string txId2 = null;
1361             string result;
1362             using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
1363             {
1364                 txId1 = AssertAndGetCurrentTransactionId();
1365                 result = await DoAsyncWorkAsync(txId1);
1366                 txId2 = AssertAndGetCurrentTransactionId();
1367 
1368                 scope.Complete();
1369             }
1370 
1371             VerifyTxId(requiresNew, txId, txId1, txId2);
1372 
1373             return result;
1374         }
1375 
DoAsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, bool asyncWorkBeforeChild, bool asyncWorkAfterChild, string txId)1376         private static async Task<string> DoAsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, bool asyncWorkBeforeChild, bool asyncWorkAfterChild, string txId)
1377         {
1378             string txId1 = null;
1379             string txId2 = null;
1380             string txId3 = null;
1381             string result;
1382             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
1383             {
1384                 txId1 = AssertAndGetCurrentTransactionId();
1385 
1386                 if (asyncWorkBeforeChild)
1387                 {
1388                     result = await DoAsyncWorkAsync(txId1);
1389                 }
1390 
1391                 if (asyncChild)
1392                 {
1393                     result = await DoAsyncTxWorkAsync(childRequiresNew, txId1);
1394                 }
1395                 else
1396                 {
1397                     result = DoSyncTxWork(childRequiresNew, txId1);
1398                 }
1399 
1400                 txId3 = AssertAndGetCurrentTransactionId();
1401 
1402                 if (asyncWorkAfterChild)
1403                 {
1404                     result = await DoAsyncWorkAsync(txId1);
1405                 }
1406 
1407                 txId2 = AssertAndGetCurrentTransactionId();
1408 
1409                 scope.Complete();
1410             }
1411 
1412             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1413             VerifyTxId(parentRequiresNew, txId, txId1, txId3);
1414 
1415             return result;
1416         }
1417 
SyncTSL2NestedTxWork(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, string txId)1418         private static string SyncTSL2NestedTxWork(bool parentRequiresNew, bool childRequiresNew, bool asyncChild, string txId)
1419         {
1420             string txId1 = null;
1421             string txId2 = null;
1422             Task<string> task = null;
1423             string result = null;
1424             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
1425             {
1426                 txId1 = AssertAndGetCurrentTransactionId();
1427 
1428                 if (asyncChild)
1429                 {
1430                     task = DoAsyncTxWorkAsync(childRequiresNew, txId1);
1431                 }
1432                 else
1433                 {
1434                     result = DoSyncTxWork(childRequiresNew, txId1);
1435                 }
1436 
1437                 if (asyncChild)
1438                 {
1439                     result = task.Result;
1440                 }
1441 
1442                 txId2 = AssertAndGetCurrentTransactionId();
1443 
1444                 scope.Complete();
1445             }
1446 
1447             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1448 
1449             return result;
1450         }
1451 
SyncTSL3AsyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId)1452         private static void SyncTSL3AsyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId)
1453         {
1454             string txId1;
1455             string txId2;
1456 
1457             AssertTransaction(txId);
1458 
1459             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
1460             {
1461                 txId1 = AssertAndGetCurrentTransactionId();
1462                 Task<string> task = DoAsyncTSL2NestedTxWorkAsync(childL2RequiresNew, childL3RequiresNew, asyncChildL3, asyncWorkBeforeChildL3, asyncWorkAfterChildL3, txId1);
1463                 txId2 = AssertAndGetCurrentTransactionId();
1464 
1465                 task.Wait();
1466                 scope.Complete();
1467             }
1468 
1469             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1470             AssertTransaction(txId);
1471         }
1472 
SyncTSL3SyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, string txId)1473         private static void SyncTSL3SyncTSL2NestedTxWork(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, string txId)
1474         {
1475             string txId1;
1476             string txId2;
1477 
1478             AssertTransaction(txId);
1479 
1480             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
1481             {
1482                 txId1 = AssertAndGetCurrentTransactionId();
1483                 SyncTSL2NestedTxWork(childL2RequiresNew, childL3RequiresNew, asyncChildL3, txId1);
1484                 txId2 = AssertAndGetCurrentTransactionId();
1485                 scope.Complete();
1486             }
1487 
1488             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1489             AssertTransaction(txId);
1490         }
1491 
DoAsyncTSL3AsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId)1492         private static async Task<string> DoAsyncTSL3AsyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL3, bool asyncWorkAfterChildL3, string txId)
1493         {
1494             string txId1;
1495             string txId2;
1496             string result = null;
1497             AssertTransaction(txId);
1498 
1499             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
1500             {
1501                 txId1 = AssertAndGetCurrentTransactionId();
1502                 result = await DoAsyncTSL2NestedTxWorkAsync(childL2RequiresNew, childL3RequiresNew, asyncChildL3, asyncWorkBeforeChildL3, asyncWorkAfterChildL3, txId1);
1503 
1504                 txId2 = AssertAndGetCurrentTransactionId();
1505 
1506                 scope.Complete();
1507             }
1508 
1509             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1510 
1511             return result;
1512         }
1513 
DoAsyncTSL3SyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL2, bool asyncWorkAfterChildL2, string txId)1514         private static async Task<string> DoAsyncTSL3SyncTSL2NestedTxWorkAsync(bool parentRequiresNew, bool childL2RequiresNew, bool childL3RequiresNew, bool asyncChildL3, bool asyncWorkBeforeChildL2, bool asyncWorkAfterChildL2, string txId)
1515         {
1516             string txId1;
1517             string txId2;
1518             string txId3;
1519             string result = null;
1520 
1521             AssertTransaction(txId);
1522 
1523             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
1524             {
1525                 txId1 = AssertAndGetCurrentTransactionId();
1526 
1527                 if (asyncWorkBeforeChildL2)
1528                 {
1529                     await DoAsyncWorkAsync(txId1);
1530                 }
1531 
1532                 result = SyncTSL2NestedTxWork(childL2RequiresNew, childL3RequiresNew, asyncChildL3, txId1);
1533 
1534                 txId3 = AssertAndGetCurrentTransactionId();
1535 
1536                 if (asyncWorkAfterChildL2)
1537                 {
1538                     await DoAsyncWorkAsync(txId1);
1539                 }
1540 
1541                 txId2 = AssertAndGetCurrentTransactionId();
1542 
1543                 scope.Complete();
1544             }
1545 
1546             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1547             VerifyTxId(parentRequiresNew, txId, txId1, txId3);
1548             AssertTransaction(txId);
1549 
1550             return result;
1551         }
1552 
DoAsyncWorkAsync(string txId)1553         private static async Task<string> DoAsyncWorkAsync(string txId)
1554         {
1555             string txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null;
1556             Assert.Equal(txId, txId1);
1557 
1558             if (s_throwExceptionDefaultOrBeforeAwait)
1559             {
1560                 throw new Exception("DoAsyncWorkAsync exception before await");
1561             }
1562 
1563             await Task.Delay(2);
1564 
1565             Task<string> getString = Task.Run(delegate
1566             {
1567                 txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null;
1568                 Assert.Equal(txId, txId1);
1569                 return DoWork(txId);
1570             });
1571 
1572             if (s_throwExceptionAfterAwait)
1573             {
1574                 throw new Exception("DoAsyncWorkAsync exception after await");
1575             }
1576 
1577             string result = await getString;
1578 
1579             txId1 = Transaction.Current != null ? Transaction.Current.TransactionInformation.LocalIdentifier : null;
1580             Assert.Equal(txId, txId1);
1581 
1582             return result;
1583         }
1584 
SyncTSDoTaskUnderAsyncTS(bool parentRequiresNew, bool childRequiresNew, string txId)1585         private static void SyncTSDoTaskUnderAsyncTS(bool parentRequiresNew, bool childRequiresNew, string txId)
1586         {
1587             string txId1 = null;
1588             string txId2 = null;
1589             AssertTransaction(txId);
1590 
1591             using (TransactionScope scope = new TransactionScope(parentRequiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required))
1592             {
1593                 txId1 = AssertAndGetCurrentTransactionId();
1594                 DoTaskUnderAsyncTS(childRequiresNew, txId1);
1595                 txId2 = AssertAndGetCurrentTransactionId();
1596                 scope.Complete();
1597             }
1598 
1599             VerifyTxId(parentRequiresNew, txId, txId1, txId2);
1600             AssertTransaction(txId);
1601         }
1602 
DoTaskUnderAsyncTS(bool requiresNew, string txId)1603         private static void DoTaskUnderAsyncTS(bool requiresNew, string txId)
1604         {
1605             string txId1 = null;
1606             string txId2 = null;
1607             AssertTransaction(txId);
1608 
1609             using (TransactionScope scope = new TransactionScope(requiresNew ? TransactionScopeOption.RequiresNew : TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
1610             {
1611                 txId1 = AssertAndGetCurrentTransactionId();
1612                 DoALotOfWork(txId1);
1613                 txId2 = AssertAndGetCurrentTransactionId();
1614                 scope.Complete();
1615             }
1616 
1617             VerifyTxId(requiresNew, txId, txId1, txId2);
1618             AssertTransaction(txId);
1619         }
1620 
DoALotOfWork(string txId)1621         private static void DoALotOfWork(string txId)
1622         {
1623             Task[] a = new Task[32];
1624             for (int i = 0; i < 32; i++)
1625             {
1626                 a[i] = new Task(() => DoWork(txId));
1627                 a[i].Start();
1628             }
1629 
1630             Task.WaitAll(a);
1631         }
1632 
VerifyTxId(bool requiresNew, string parentTxId, string beforeTxId, string afterTxId)1633         public static void VerifyTxId(bool requiresNew, string parentTxId, string beforeTxId, string afterTxId)
1634         {
1635             Assert.Equal(beforeTxId, afterTxId);
1636             if (requiresNew)
1637             {
1638                 Assert.NotEqual(parentTxId, beforeTxId);
1639             }
1640             else
1641             {
1642                 if (!string.IsNullOrEmpty(parentTxId))
1643                 {
1644                     Assert.Equal(parentTxId, beforeTxId);
1645                 }
1646             }
1647         }
1648 
AssertTransaction(string txId)1649         public static void AssertTransaction(string txId)
1650         {
1651             if (string.IsNullOrEmpty(txId))
1652             {
1653                 AssertTransactionNull();
1654             }
1655             else
1656             {
1657                 Assert.Equal(txId, AssertAndGetCurrentTransactionId());
1658             }
1659         }
AssertTransactionNull()1660         public static void AssertTransactionNull()
1661         {
1662             Assert.Equal(null, Transaction.Current);
1663         }
1664 
AssertTransactionNotNull()1665         public static void AssertTransactionNotNull()
1666         {
1667             Assert.NotEqual(null, Transaction.Current);
1668         }
1669 
AssertAndGetCurrentTransactionId()1670         public static string AssertAndGetCurrentTransactionId()
1671         {
1672             AssertTransactionNotNull();
1673             return Transaction.Current.TransactionInformation.LocalIdentifier;
1674         }
1675 
AssertAndGetCurrentTransactionId(TransactionScopeOption scopeOption)1676         public static string AssertAndGetCurrentTransactionId(TransactionScopeOption scopeOption)
1677         {
1678             if (scopeOption == TransactionScopeOption.Suppress)
1679             {
1680                 AssertTransactionNull();
1681                 return null;
1682             }
1683             else
1684             {
1685                 AssertTransactionNotNull();
1686                 return Transaction.Current.TransactionInformation.LocalIdentifier;
1687             }
1688         }
1689 
AssertTransactionNullAndWaitTask(Task<string> task)1690         public static void AssertTransactionNullAndWaitTask(Task<string> task)
1691         {
1692             AssertTransactionNull();
1693             task.Wait();
1694             AssertTransactionNull();
1695         }
1696 
AssertTransactionNullAndWaitTask(Task task)1697         public static void AssertTransactionNullAndWaitTask(Task task)
1698         {
1699             AssertTransactionNull();
1700             task.Wait();
1701             AssertTransactionNull();
1702         }
1703 
SetExceptionInjection(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait)1704         public static void SetExceptionInjection(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait)
1705         {
1706             s_throwExceptionDefaultOrBeforeAwait = exceptionDefaultOrBeforeAwait;
1707             s_throwExceptionAfterAwait = exceptionAfterAwait;
1708         }
1709 
ResetExceptionInjection()1710         public static void ResetExceptionInjection()
1711         {
1712             s_throwExceptionDefaultOrBeforeAwait = false;
1713             s_throwExceptionAfterAwait = false;
1714         }
1715 
HandleException(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait, Action action)1716         public static void HandleException(bool exceptionDefaultOrBeforeAwait, bool exceptionAfterAwait, Action action)
1717         {
1718             bool hasException = false;
1719 
1720             SetExceptionInjection(exceptionDefaultOrBeforeAwait, exceptionAfterAwait);
1721             try
1722             {
1723                 action.Invoke();
1724             }
1725             catch (Exception ex)
1726             {
1727                 hasException = true;
1728                 Debug.WriteLine("Exception: {0}", ex.Message);
1729             }
1730 
1731             AssertTransactionNull();
1732             Assert.Equal<bool>(hasException, true);
1733             ResetExceptionInjection();
1734         }
1735     }
1736 
1737     public class ThreadSyncObject
1738     {
1739         public string Id
1740         {
1741             get;
1742             set;
1743         }
1744 
1745         public ManualResetEvent Event
1746         {
1747             get;
1748             set;
1749         }
1750 
1751         public TransactionScopeAsyncFlowOption RootAsyncFlowOption
1752         {
1753             get;
1754             set;
1755         }
1756     }
1757 
1758     public interface IStatus
1759     {
GetStatus(string id, bool fail)1760         string GetStatus(string id, bool fail);
1761     }
1762 }
1763