1 // NpgsqlPromotableSinglePhaseNotification.cs
4 // Josh Cooley <jbnpgsql@tuxinthebox.net>
6 // Copyright (C) 2007, The Npgsql Development Team
8 // Permission to use, copy, modify, and distribute this software and its
9 // documentation for any purpose, without fee, and without a written
10 // agreement is hereby granted, provided that the above copyright notice
11 // and this paragraph and the following two paragraphs appear in all copies.
13 // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
14 // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
15 // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
16 // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
17 // THE POSSIBILITY OF SUCH DAMAGE.
19 // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
20 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21 // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
23 // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
26 using System.Transactions;
30 internal class NpgsqlPromotableSinglePhaseNotification : IPromotableSinglePhaseNotification
32 private readonly NpgsqlConnection _connection;
33 private IsolationLevel _isolationLevel;
34 private NpgsqlTransaction _npgsqlTx;
35 private NpgsqlTransactionCallbacks _callbacks;
36 private INpgsqlResourceManager _rm;
37 private bool _inTransaction;
39 private static readonly String CLASSNAME = "NpgsqlPromotableSinglePhaseNotification";
41 public NpgsqlPromotableSinglePhaseNotification(NpgsqlConnection connection)
43 _connection = connection;
46 public void Enlist(Transaction tx)
48 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Enlist");
51 _isolationLevel = tx.IsolationLevel;
52 if (!tx.EnlistPromotableSinglePhase(this))
54 // must already have a durable resource
56 _npgsqlTx = _connection.BeginTransaction(ConvertIsolationLevel(_isolationLevel));
57 _inTransaction = true;
58 _rm = CreateResourceManager();
59 _callbacks = new NpgsqlTransactionCallbacks(_connection);
60 _rm.Enlist(_callbacks, TransactionInterop.GetTransmitterPropagationToken(tx));
61 // enlisted in distributed transaction
62 // disconnect and cleanup local transaction
71 /// Used when a connection is closed
75 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Prepare");
78 // may not be null if Promote or Enlist is called first
79 if (_callbacks == null)
81 _callbacks = new NpgsqlTransactionCallbacks(_connection);
83 _callbacks.PrepareTransaction();
84 if (_npgsqlTx != null)
86 // cancel the NpgsqlTransaction since this will
87 // be handled by a two phase commit.
95 #region IPromotableSinglePhaseNotification Members
97 public void Initialize()
99 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Initialize");
100 _npgsqlTx = _connection.BeginTransaction(ConvertIsolationLevel(_isolationLevel));
101 _inTransaction = true;
104 public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment)
106 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Rollback");
107 // try to rollback the transaction with either the
108 // ADO.NET transaction or the callbacks that managed the
109 // two phase commit transaction.
110 if (_npgsqlTx != null)
112 _npgsqlTx.Rollback();
115 singlePhaseEnlistment.Aborted();
117 else if (_callbacks != null)
121 _rm.RollbackWork(_callbacks.GetName());
122 singlePhaseEnlistment.Aborted();
126 _callbacks.RollbackTransaction();
127 singlePhaseEnlistment.Aborted();
131 _inTransaction = false;
134 public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)
136 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "SinglePhaseCommit");
137 if (_npgsqlTx != null)
142 singlePhaseEnlistment.Committed();
144 else if (_callbacks != null)
148 _rm.CommitWork(_callbacks.GetName());
149 singlePhaseEnlistment.Committed();
153 _callbacks.CommitTransaction();
154 singlePhaseEnlistment.Committed();
158 _inTransaction = false;
163 #region ITransactionPromoter Members
165 public byte[] Promote()
167 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Promote");
168 _rm = CreateResourceManager();
169 // may not be null if Prepare or Enlist is called first
170 if (_callbacks == null)
172 _callbacks = new NpgsqlTransactionCallbacks(_connection);
174 byte[] token = _rm.Promote(_callbacks);
175 // mostly likely case for this is the transaction has been prepared.
176 if (_npgsqlTx != null)
178 // cancel the NpgsqlTransaction since this will
179 // be handled by a two phase commit.
189 private static INpgsqlResourceManager _resourceManager;
191 private static INpgsqlResourceManager CreateResourceManager()
193 // TODO: create network proxy for resource manager
194 if (_resourceManager == null)
196 AppDomain rmDomain = AppDomain.CreateDomain("NpgsqlResourceManager", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
198 (INpgsqlResourceManager)
199 rmDomain.CreateInstanceAndUnwrap(typeof (NpgsqlResourceManager).Assembly.FullName,
200 typeof (NpgsqlResourceManager).FullName);
202 return _resourceManager;
203 //return new NpgsqlResourceManager();
206 private static System.Data.IsolationLevel ConvertIsolationLevel(IsolationLevel _isolationLevel)
208 switch (_isolationLevel)
210 case IsolationLevel.Chaos:
211 return System.Data.IsolationLevel.Chaos;
212 case IsolationLevel.ReadCommitted:
213 return System.Data.IsolationLevel.ReadCommitted;
214 case IsolationLevel.ReadUncommitted:
215 return System.Data.IsolationLevel.ReadUncommitted;
216 case IsolationLevel.RepeatableRead:
217 return System.Data.IsolationLevel.RepeatableRead;
218 case IsolationLevel.Serializable:
219 return System.Data.IsolationLevel.Serializable;
220 case IsolationLevel.Snapshot:
221 return System.Data.IsolationLevel.Snapshot;
222 case IsolationLevel.Unspecified:
224 return System.Data.IsolationLevel.Unspecified;