1: /// <summary>
2: /// The Transaction Manager holds a list of active, isolated transactions within the system and acts as the entry point for a new unit of work scope.
3: /// </summary>
4: public class TransactionManager : ITransactionManager, IDisposable
5: {
6: /// <summary>
7: /// Dictionary of operation contexts to transactions operating under that context
8: /// </summary>
9: private readonly Dictionary<Guid, List<IUnitOfWorkTransaction>> transactionLists = new Dictionary<Guid, List<IUnitOfWorkTransaction>>();
10:
11: [ThreadStatic]
12: private static Guid operationContext;
13:
14: private bool disposed;
15: private object transactionsLockObject = new object();
16: private IUnitOfWorkTransactionFactory transactionFactory;
17:
18: /// <summary>
19: /// Initializes a new instance of the <see cref="TransactionManager"/> class.
20: /// </summary>
21: /// <param name="transactionFactory">The transaction factory.</param>
22: public TransactionManager(IUnitOfWorkTransactionFactory transactionFactory)
23: {
24: this.transactionFactory = transactionFactory;
25: }
26:
27: /// <summary>
28: /// Gets the current context of the most recent transaction's unit of work.
29: /// </summary>
30: /// <value>The current context.</value>
31: public ObjectContext CurrentContext
32: {
33: get
34: {
35: if (CurrentTransaction != null)
36: {
37: return CurrentTransaction.UnitOfWork.Context;
38: }
39: else
40: {
41: return null;
42: }
43: }
44: }
45:
46: /// <summary>
47: /// Gets or sets the operation context that the current thread is running under.
48: /// Transactions are tied to an operation context, so that additional scopes that join a transaction should only join if they belong to the same context.
49: /// This value is currently being set in the AuthorizationPolicy so that each call to WCF tags its thread with a unique context. If new threads are spawned
50: /// by processes, they must manually copy the operation context from the main thread to their sub-threads.
51: /// </summary>
52: /// <value>The operation context, unique per wcf call.</value>
53: /// <param name="operationContext">The operation context.</param>
54: public Guid OperationContext
55: {
56: get
57: {
58: return operationContext;
59: }
60:
61: set
62: {
63: operationContext = value;
64: }
65: }
66:
67: /// <summary>
68: /// Gets the most recent transaction.
69: /// </summary>
70: /// <value>The current transaction.</value>
71: private IUnitOfWorkTransaction CurrentTransaction
72: {
73: get
74: {
75: lock (transactionsLockObject)
76: {
77: List<IUnitOfWorkTransaction> transactions;
78: if (transactionLists.TryGetValue(OperationContext, out transactions))
79: {
80: if (transactions.Count > 0)
81: {
82: return transactions[transactions.Count - 1];
83: }
84: }
85:
86: return null;
87: }
88: }
89: }
90:
91: /// <summary>
92: /// Enlists a <see cref="UnitOfWorkScope"/> instance with the transaction manager.
93: /// </summary>
94: /// <param name="scope">bool. True if the scope should be enlisted in a new transaction, else
95: /// false if the scope should participate in the existing transaction</param>
96: /// <param name="newTransaction">if set to <c>true</c> [new transaction].</param>
97: /// <returns>
98: /// Transcation created or assigned to the scope.
99: /// </returns>
100: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed outside of this scope.")]
101: public IUnitOfWorkTransaction EnlistScope(IUnitOfWorkScope scope, bool newTransaction)
102: {
103: if (newTransaction || CurrentTransaction == null)
104: {
105: IUnitOfWorkTransaction transaction = transactionFactory.CreateUnitOfWorkTransaction(OperationContext);
106: transaction.TransactionDisposing += OnTransactionDisposing;
107:
108: lock (transactionsLockObject)
109: {
110: transaction.EnlistScope(scope);
111: List<IUnitOfWorkTransaction> transactions;
112: if (!transactionLists.TryGetValue(OperationContext, out transactions))
113: {
114: transactions = new List<IUnitOfWorkTransaction>();
115: transactionLists.Add(OperationContext, transactions);
116: }
117:
118: transactions.Add(transaction);
119: }
120:
121: return transaction;
122: }
123: else
124: {
125: CurrentTransaction.EnlistScope(scope);
126: return CurrentTransaction;
127: }
128: }
129:
130: /// <summary>
131: /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
132: /// </summary>
133: /// <filterpriority>2</filterpriority>
134: public void Dispose()
135: {
136: Dispose(true);
137: GC.SuppressFinalize(this);
138: }
139:
140: /// <summary>
141: /// Releases unmanaged and - optionally - managed resources
142: /// </summary>
143: /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
144: private void Dispose(bool disposing)
145: {
146: if (disposed)
147: {
148: return;
149: }
150:
151: if (disposing)
152: {
153: lock (transactionsLockObject)
154: {
155: if (transactionLists != null && transactionLists.Count > 0)
156: {
157: foreach (var kvp in transactionLists)
158: {
159: foreach (var transaction in kvp.Value)
160: {
161: transaction.TransactionDisposing -= OnTransactionDisposing;
162: transaction.Dispose();
163: }
164: }
165:
166: transactionLists.Clear();
167: }
168: }
169: }
170:
171: disposed = true;
172: }
173:
174: /// <summary>
175: /// Called when [transaction disposing].
176: /// </summary>
177: /// <param name="sender">The sender.</param>
178: /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
179: private void OnTransactionDisposing(object sender, EventArgs e)
180: {
181: IUnitOfWorkTransaction transaction = sender as IUnitOfWorkTransaction;
182: if (transaction != null)
183: {
184: transaction.TransactionDisposing -= OnTransactionDisposing;
185: lock (transactionsLockObject)
186: {
187: List<IUnitOfWorkTransaction> transactions;
188: if (transactionLists.TryGetValue(transaction.OperationContext, out transactions))
189: {
190: if (transactions.Contains(transaction))
191: {
192: transactions.Remove(transaction);
193: }
194: }
195: }
196: }
197: }