001 | 下载 SQLiteTransaction.cs
|
002 | * ADO.NET 2.0 Data Provider for SQLite Version 3.X |
003 | * Written by Robert Simpson (robert@blackcastlesoft.com) |
004 | * |
005 | * Released to the public domain, use at your own risk! |
006 | ********************************************************/ |
007 |
008 | namespace Mono.Data.Sqlite |
009 | { |
010 | using System; |
011 | using System.Data; |
012 | using System.Data.Common; |
013 |
014 | /// <summary> |
015 | /// SQLite implementation of DbTransaction. |
016 | /// </summary> |
017 | public sealed class SqliteTransaction : DbTransaction |
018 | { |
019 | /// <summary> |
020 | /// The connection to which this transaction is bound |
021 | /// </summary> |
022 | internal SqliteConnection _cnn; |
023 | internal long _version; // Matches the version of the connection |
024 | private IsolationLevel _level; |
025 |
026 | /// <summary> |
027 | /// Constructs the transaction object, binding it to the supplied connection |
028 | /// </summary> |
029 | /// <param name="connection">The connection to open a transaction on</param> |
030 | /// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param> |
031 | internal SqliteTransaction(SqliteConnection connection, bool deferredLock) |
032 | { |
033 | _cnn = connection; |
034 | _version = _cnn._version; |
035 |
036 | _level = (deferredLock == true ) ? IsolationLevel.ReadCommitted : IsolationLevel.Serializable; |
037 |
038 | if (_cnn._transactionLevel++ == 0) |
039 | { |
040 | try |
041 | { |
042 | using (SqliteCommand cmd = _cnn.CreateCommand()) |
043 | { |
044 | if (!deferredLock) |
045 | cmd.CommandText = "BEGIN IMMEDIATE" ; |
046 | else |
047 | cmd.CommandText = "BEGIN" ; |
048 |
049 | cmd.ExecuteNonQuery(); |
050 | } |
051 | } |
052 | catch (SqliteException) |
053 | { |
054 | _cnn._transactionLevel--; |
055 | _cnn = null ; |
056 | throw ; |
057 | } |
058 | } |
059 | } |
060 |
061 | /// <summary> |
062 | /// Commits the current transaction. |
063 | /// </summary> |
064 | public override void Commit() |
065 | { |
066 | IsValid( true ); |
067 |
068 | if (_cnn._transactionLevel - 1 == 0) |
069 | { |
070 | using (SqliteCommand cmd = _cnn.CreateCommand()) |
071 | { |
072 | cmd.CommandText = "COMMIT" ; |
073 | cmd.ExecuteNonQuery(); |
074 | } |
075 | } |
076 | _cnn._transactionLevel--; |
077 | _cnn = null ; |
078 | } |
079 |
080 | /// <summary> |
081 | /// Returns the underlying connection to which this transaction applies. |
082 | /// </summary> |
083 | public new SqliteConnection Connection |
084 | { |
085 | get { return _cnn; } |
086 | } |
087 |
088 | /// <summary> |
089 | /// Forwards to the local Connection property |
090 | /// </summary> |
091 | protected override DbConnection DbConnection |
092 | { |
093 | get { return Connection; } |
094 | } |
095 |
096 | /// <summary> |
097 | /// Disposes the transaction. If it is currently active, any changes are rolled back. |
098 | /// </summary> |
099 | protected override void Dispose( bool disposing) |
100 | { |
101 | if (disposing) |
102 | { |
103 | lock ( this ) |
104 | { |
105 | if (IsValid( false )) |
106 | Rollback(); |
107 |
108 | _cnn = null ; |
109 | } |
110 | } |
111 | base .Dispose(disposing); |
112 | } |
113 |
114 | /// <summary> |
115 | /// Gets the isolation level of the transaction. SQLite only supports Serializable transactions. |
116 | /// </summary> |
117 | public override IsolationLevel IsolationLevel |
118 | { |
119 | get { return _level; } |
120 | } |
121 |
122 | /// <summary> |
123 | /// Rolls back the active transaction. |
124 | /// </summary> |
125 | public override void Rollback() |
126 | { |
127 | IsValid( true ); |
128 |
129 | IssueRollback(_cnn); |
130 |
131 | _cnn._transactionLevel = 0; |
132 | _cnn = null ; |
133 | } |
134 |
135 | internal static void IssueRollback(SqliteConnection cnn) |
136 | { |
137 | using (SqliteCommand cmd = cnn.CreateCommand()) |
138 | { |
139 | cmd.CommandText = "ROLLBACK" ; |
140 | cmd.ExecuteNonQuery(); |
141 | } |
142 | } |
143 |
144 | internal bool IsValid( bool throwError) |
145 | { |
146 | if (_cnn == null ) |
147 | { |
148 | if (throwError == true ) throw new ArgumentNullException( "No connection associated with this transaction" ); |
149 | else return false ; |
150 | } |
151 |
152 | if (_cnn._transactionLevel == 0) |
153 | { |
154 | if (throwError == true ) throw new SqliteException(( int )SQLiteErrorCode.Misuse, "No transaction is active on this connection" ); |
155 | else return false ; |
156 | } |
157 | if (_cnn._version != _version) |
158 | { |
159 | if (throwError == true ) throw new SqliteException(( int )SQLiteErrorCode.Misuse, "The connection was closed and re-opened, changes were rolled back" ); |
160 | else return false ; |
161 | } |
162 | if (_cnn.State != ConnectionState.Open) |
163 | { |
164 | if (throwError == true ) throw new SqliteException(( int )SQLiteErrorCode.Misuse, "Connection was closed" ); |
165 | else return false ; |
166 | } |
167 |
168 | return true ; |
169 | } |
170 | } |
171 | } |
001 | /********************************************************* |
002 | * ADO.NET 2.0 Data Provider for SQLite Version 3.X |
003 | * Written by Robert Simpson (robert@blackcastlesoft.com) |
004 | * |
005 | * Released to the public domain, use at your own risk! |
006 | ********************************************************/ |
007 |
008 | namespace Mono.Data.Sqlite |
009 | { |
010 | using System; |
011 | using System.Data; |
012 | using System.Data.Common; |
013 |
014 | /// <summary> |
015 | /// SQLite implementation of DbTransaction. |
016 | /// </summary> |
017 | public sealed class SqliteTransaction : DbTransaction |
018 | { |
019 | /// <summary> |
020 | /// The connection to which this transaction is bound |
021 | /// </summary> |
022 | internal SqliteConnection _cnn; |
023 | internal long _version; // Matches the version of the connection |
024 | private IsolationLevel _level; |
025 |
026 | /// <summary> |
027 | /// Constructs the transaction object, binding it to the supplied connection |
028 | /// </summary> |
029 | /// <param name="connection">The connection to open a transaction on</param> |
030 | /// <param name="deferredLock">TRUE to defer the writelock, or FALSE to lock immediately</param> |
031 | internal SqliteTransaction(SqliteConnection connection, bool deferredLock) |
032 | { |
033 | _cnn = connection; |
034 | _version = _cnn._version; |
035 |
036 | _level = (deferredLock == true ) ? IsolationLevel.ReadCommitted : IsolationLevel.Serializable; |
037 |
038 | if (_cnn._transactionLevel++ == 0) |
039 | { |
040 | try |
041 | { |
042 | using (SqliteCommand cmd = _cnn.CreateCommand()) |
043 | { |
044 | if (!deferredLock) |
045 | cmd.CommandText = "BEGIN IMMEDIATE" ; |
046 | else |
047 | cmd.CommandText = "BEGIN" ; |
048 |
049 | cmd.ExecuteNonQuery(); |
050 | } |
051 | } |
052 | catch (SqliteException) |
053 | { |
054 | _cnn._transactionLevel--; |
055 | _cnn = null ; |
056 | throw ; |
057 | } |
058 | } |
059 | } |
060 |
061 | /// <summary> |
062 | /// Commits the current transaction. |
063 | /// </summary> |
064 | public override void Commit() |
065 | { |
066 | IsValid( true ); |
067 |
068 | if (_cnn._transactionLevel - 1 == 0) |
069 | { |
070 | using (SqliteCommand cmd = _cnn.CreateCommand()) |
071 | { |
072 | cmd.CommandText = "COMMIT" ; |
073 | cmd.ExecuteNonQuery(); |
074 | } |
075 | } |
076 | _cnn._transactionLevel--; |
077 | _cnn = null ; |
078 | } |
079 |
080 | /// <summary> |
081 | /// Returns the underlying connection to which this transaction applies. |
082 | /// </summary> |
083 | public new SqliteConnection Connection |
084 | { |
085 | get { return _cnn; } |
086 | } |
087 |
088 | /// <summary> |
089 | /// Forwards to the local Connection property |
090 | /// </summary> |
091 | protected override DbConnection DbConnection |
092 | { |
093 | get { return Connection; } |
094 | } |
095 |
096 | /// <summary> |
097 | /// Disposes the transaction. If it is currently active, any changes are rolled back. |
098 | /// </summary> |
099 | protected override void Dispose( bool disposing) |
100 | { |
101 | if (disposing) |
102 | { |
103 | lock ( this ) |
104 | { |
105 | if (IsValid( false )) |
106 | Rollback(); |
107 |
108 | _cnn = null ; |
109 | } |
110 | } |
111 | base .Dispose(disposing); |
112 | } |
113 |
114 | /// <summary> |
115 | /// Gets the isolation level of the transaction. SQLite only supports Serializable transactions. |
116 | /// </summary> |
117 | public override IsolationLevel IsolationLevel |
118 | { |
119 | get { return _level; } |
120 | } |
121 |
122 | /// <summary> |
123 | /// Rolls back the active transaction. |
124 | /// </summary> |
125 | public override void Rollback() |
126 | { |
127 | IsValid( true ); |
128 |
129 | IssueRollback(_cnn); |
130 |
131 | _cnn._transactionLevel = 0; |
132 | _cnn = null ; |
133 | } |
134 |
135 | internal static void IssueRollback(SqliteConnection cnn) |
136 | { |
137 | using (SqliteCommand cmd = cnn.CreateCommand()) |
138 | { |
139 | cmd.CommandText = "ROLLBACK" ; |
140 | cmd.ExecuteNonQuery(); |
141 | } |
142 | } |
143 |
144 | internal bool IsValid( bool throwError) |
145 | { |
146 | if (_cnn == null ) |
147 | { |
148 | if (throwError == true ) throw new ArgumentNullException( "No connection associated with this transaction" ); |
149 | else return false ; |
150 | } |
151 |
152 | if (_cnn._transactionLevel == 0) |
153 | { |
154 | if (throwError == true ) throw new SqliteException(( int )SQLiteErrorCode.Misuse, "No transaction is active on this connection" ); |
155 | else return false ; |
156 | } |
157 | if (_cnn._version != _version) |
158 | { |
159 | if (throwError == true ) throw new SqliteException(( int )SQLiteErrorCode.Misuse, "The connection was closed and re-opened, changes were rolled back" ); |
160 | else return false ; |
161 | } |
162 | if (_cnn.State != ConnectionState.Open) |
163 | { |
164 | if (throwError == true ) throw new SqliteException(( int )SQLiteErrorCode.Misuse, "Connection was closed" ); |
165 | else return false ; |
166 | } |
167 |
168 | return true ; |
169 | } |
170 | } |
171 | } |