Index: SQLite.NET.Settings.targets ================================================================== --- SQLite.NET.Settings.targets +++ SQLite.NET.Settings.targets @@ -104,10 +104,18 @@ false + + + true + (('$(IsCompactFramework)' == 'false' And Exists('$(SQLiteNetDir)\System.Data.SQLite\System.Data.SQLite.snk')) Or + ('$(IsCompactFramework)' != 'false' And Exists('$(SQLiteNetDir)\System.Data.SQLite\System.Data.SQLite.CF.snk')))"> true Index: System.Data.SQLite/AssemblyInfo.cs ================================================================== --- System.Data.SQLite/AssemblyInfo.cs +++ System.Data.SQLite/AssemblyInfo.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Resources; @@ -31,11 +38,11 @@ // Setting ComVisible to false makes the types in this assembly not visible // to COM componenets. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("System.Data.SQLite.Linq, PublicKey=002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0a25a843266973704137cbb0e7441c1fe7cae4e2440ae91ab8cde3933febcb1ac48dd33b40e13c421d8215c18a4349a436dd499e3c385cc683015f886f6c10bd90115eb2bd61b67750839e3a19941dc9c")] +[assembly: InternalsVisibleTo("System.Data.SQLite.Linq, PublicKey=" + System.Data.SQLite.SQLite3.PublicKey)] [assembly: NeutralResourcesLanguage("en")] #if !PLATFORM_COMPACTFRAMEWORK [assembly: AllowPartiallyTrustedCallers] [assembly: ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] Index: System.Data.SQLite/SQLite3.cs ================================================================== --- System.Data.SQLite/SQLite3.cs +++ System.Data.SQLite/SQLite3.cs @@ -21,10 +21,20 @@ /// /// This class implements SQLiteBase completely, and is the guts of the code that interop's SQLite with .NET /// internal class SQLite3 : SQLiteBase { + // + // NOTE: This is the public key for the System.Data.SQLite assembly. If you change the + // SNK file, you will need to change this as well. + // + internal const string PublicKey = + "002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1" + + "b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0" + + "a25a843266973704137cbb0e7441c1fe7cae4e2440ae91ab8cde3933febcb1ac48dd33b40e13c421" + + "d8215c18a4349a436dd499e3c385cc683015f886f6c10bd90115eb2bd61b67750839e3a19941dc9c"; + #if !PLATFORM_COMPACTFRAMEWORK internal const string DesignerVersion = "1.0.77.0"; #endif /// @@ -46,34 +56,77 @@ internal SQLite3(SQLiteDateFormats fmt, DateTimeKind kind) : base(fmt, kind) { } - protected override void Dispose(bool bDisposing) + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ { - Close(); +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLite3).Name); +#endif } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + Close(); + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// // It isn't necessary to cleanup any functions we've registered. If the connection // goes to the pool and is resurrected later, re-registered functions will overwrite the // previous functions. The SQLiteFunctionCookieHandle will take care of freeing unmanaged // resources belonging to the previously-registered functions. internal override void Close() { if (_sql != null) { - if (_usePool) - { - SQLiteBase.ResetConnection(_sql); - SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion); - } - else - _sql.Dispose(); - } - - _sql = null; - } + if (_usePool) + { + SQLiteBase.ResetConnection(_sql); + SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion); + } + else + { + _sql.Dispose(); + } + _sql = null; + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// internal override void Cancel() { UnsafeNativeMethods.sqlite3_interrupt(_sql); } Index: System.Data.SQLite/SQLite3_UTF16.cs ================================================================== --- System.Data.SQLite/SQLite3_UTF16.cs +++ System.Data.SQLite/SQLite3_UTF16.cs @@ -18,21 +18,65 @@ /// internal class SQLite3_UTF16 : SQLite3 { internal SQLite3_UTF16(SQLiteDateFormats fmt, DateTimeKind kind) : base(fmt, kind) - { - } - + { + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLite3_UTF16).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8 /// /// A pointer to a UTF-16 string /// The length (IN BYTES) of the string /// A .NET string public override string ToString(IntPtr b, int nbytelen) - { + { + CheckDisposed(); return UTF16ToString(b, nbytelen); } public static string UTF16ToString(IntPtr b, int nbytelen) { @@ -43,11 +87,11 @@ else return Marshal.PtrToStringUni(b, nbytelen / 2); } internal override void Open(string strFilename, SQLiteOpenFlagsEnum flags, int maxPoolSize, bool usePool) - { + { if (_sql != null) return; _usePool = usePool; if (usePool) { @@ -126,86 +170,86 @@ } } } internal override void Bind_Text(SQLiteStatement stmt, int index, string value) - { + { int n = UnsafeNativeMethods.sqlite3_bind_text16(stmt._sqlite_stmt, index, value, value.Length * 2, (IntPtr)(-1)); if (n > 0) throw new SQLiteException(n, SQLiteLastError()); } - internal override DateTime GetDateTime(SQLiteStatement stmt, int index) - { + internal override DateTime GetDateTime(SQLiteStatement stmt, int index) + { return ToDateTime(GetText(stmt, index)); } internal override string ColumnName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string GetText(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index), -1); #endif } internal override string ColumnOriginalName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string ColumnDatabaseName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string ColumnTableName(SQLiteStatement stmt, int index) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1); #endif } internal override string GetParamValueText(IntPtr ptr) - { + { #if !SQLITE_STANDARD int len; return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16_interop(ptr, out len), len); #else return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16(ptr), -1); #endif } internal override void ReturnError(IntPtr context, string value) - { + { UnsafeNativeMethods.sqlite3_result_error16(context, value, value.Length * 2); } - internal override void ReturnText(IntPtr context, string value) - { + internal override void ReturnText(IntPtr context, string value) + { UnsafeNativeMethods.sqlite3_result_text16(context, value, value.Length * 2, (IntPtr)(-1)); } } } Index: System.Data.SQLite/SQLiteBase.cs ================================================================== --- System.Data.SQLite/SQLiteBase.cs +++ System.Data.SQLite/SQLiteBase.cs @@ -220,19 +220,64 @@ get; } internal abstract int FileControl(string zDbName, int op, IntPtr pArg); - protected virtual void Dispose(bool bDisposing) - { - } + /////////////////////////////////////////////////////////////////////////////////////////////// + #region IDisposable Members public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteBase).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteBase() + { + Dispose(false); } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// // These statics are here for lack of a better place to put them. // They exist here because they are called during the finalization of // a SQLiteStatementHandle, SQLiteConnectionHandle, and SQLiteFunctionCookieHandle. // Therefore these functions have to be static, and have to be low-level. Index: System.Data.SQLite/SQLiteCommand.cs ================================================================== --- System.Data.SQLite/SQLiteCommand.cs +++ System.Data.SQLite/SQLiteCommand.cs @@ -141,45 +141,80 @@ if (transaction != null) Transaction = transaction; } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteCommand).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Disposes of the command and clears all member variables /// /// Whether or not the class is being explicitly or implicitly disposed protected override void Dispose(bool disposing) { - base.Dispose(disposing); - - if (disposing) - { - // If a reader is active on this command, don't destroy the command, instead let the reader do it - SQLiteDataReader reader = null; - if (_activeReader != null) - { - try - { - reader = _activeReader.Target as SQLiteDataReader; - } - catch(InvalidOperationException) - { - } - } - - if (reader != null) - { - reader._disposeCommand = true; - _activeReader = null; - return; - } - - Connection = null; - _parameterCollection.Clear(); - _commandText = null; - } - } + try + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + // If a reader is active on this command, don't destroy the command, instead let the reader do it + SQLiteDataReader reader = null; + if (_activeReader != null) + { + try + { + reader = _activeReader.Target as SQLiteDataReader; + } + catch (InvalidOperationException) + { + } + } + + if (reader != null) + { + reader._disposeCommand = true; + _activeReader = null; + return; + } + + Connection = null; + _parameterCollection.Clear(); + _commandText = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Clears and destroys all statements currently prepared /// internal void ClearCommands() @@ -281,10 +316,12 @@ /// /// Not implemented /// public override void Cancel() { + CheckDisposed(); + if (_activeReader != null) { SQLiteDataReader reader = _activeReader.Target as SQLiteDataReader; if (reader != null) reader.Cancel(); @@ -299,14 +336,17 @@ #endif public override string CommandText { get { + CheckDisposed(); return _commandText; } set { + CheckDisposed(); + if (_commandText == value) return; if (_activeReader != null && _activeReader.IsAlive) { throw new InvalidOperationException("Cannot set CommandText while a DataReader is active"); @@ -327,14 +367,16 @@ #endif public override int CommandTimeout { get { + CheckDisposed(); return _commandTimeout; } set { + CheckDisposed(); _commandTimeout = value; } } /// @@ -345,14 +387,17 @@ #endif public override CommandType CommandType { get { + CheckDisposed(); return CommandType.Text; } set { + CheckDisposed(); + if (value != CommandType.Text) { throw new NotSupportedException(); } } @@ -371,10 +416,11 @@ /// Create a new parameter /// /// public new SQLiteParameter CreateParameter() { + CheckDisposed(); return new SQLiteParameter(); } /// /// The connection associated with this command @@ -382,13 +428,15 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteConnection Connection { - get { return _cnn; } + get { CheckDisposed(); return _cnn; } set { + CheckDisposed(); + if (_activeReader != null && _activeReader.IsAlive) throw new InvalidOperationException("Cannot set Connection while a DataReader is active"); if (_cnn != null) { @@ -426,11 +474,11 @@ #if !PLATFORM_COMPACTFRAMEWORK [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] #endif public new SQLiteParameterCollection Parameters { - get { return _parameterCollection; } + get { CheckDisposed(); return _parameterCollection; } } /// /// Forwards to the local Parameters property /// @@ -449,13 +497,15 @@ #if !PLATFORM_COMPACTFRAMEWORK [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] #endif public new SQLiteTransaction Transaction { - get { return _transaction; } + get { CheckDisposed(); return _transaction; } set { + CheckDisposed(); + if (_cnn != null) { if (_activeReader != null && _activeReader.IsAlive) throw new InvalidOperationException("Cannot set Transaction while a DataReader is active"); @@ -534,10 +584,11 @@ /// /// The flags to be associated with the reader /// A SQLiteDataReader public new SQLiteDataReader ExecuteReader(CommandBehavior behavior) { + CheckDisposed(); InitializeForReader(); SQLiteDataReader rd = new SQLiteDataReader(this, behavior); _activeReader = new WeakReference(rd, false); @@ -548,10 +599,11 @@ /// Overrides the default behavior of DbDataReader to return a specialized SQLiteDataReader class /// /// A SQLiteDataReader public new SQLiteDataReader ExecuteReader() { + CheckDisposed(); return ExecuteReader(CommandBehavior.Default); } /// /// Called by the SQLiteDataReader when the data reader is closed. @@ -565,10 +617,12 @@ /// Execute the command and return the number of rows inserted/updated affected by it. /// /// public override int ExecuteNonQuery() { + CheckDisposed(); + using (SQLiteDataReader reader = ExecuteReader(CommandBehavior.SingleRow | CommandBehavior.SingleResult)) { while (reader.NextResult()) ; return reader.RecordsAffected; } @@ -579,10 +633,12 @@ /// (if present), or null if no resultset was returned. /// /// The first column of the first row of the first resultset from the query public override object ExecuteScalar() { + CheckDisposed(); + using (SQLiteDataReader reader = ExecuteReader(CommandBehavior.SingleRow | CommandBehavior.SingleResult)) { if (reader.Read()) return reader[0]; } @@ -592,10 +648,11 @@ /// /// Does nothing. Commands are prepared as they are executed the first time, and kept in prepared state afterwards. /// public override void Prepare() { + CheckDisposed(); } /// /// Sets the method the SQLiteCommandBuilder uses to determine how to update inserted or updated rows in a DataTable. /// @@ -602,14 +659,16 @@ [DefaultValue(UpdateRowSource.None)] public override UpdateRowSource UpdatedRowSource { get { + CheckDisposed(); return _updateRowSource; } set { + CheckDisposed(); _updateRowSource = value; } } /// @@ -620,14 +679,17 @@ #endif public override bool DesignTimeVisible { get { + CheckDisposed(); return _designTimeVisible; } set { + CheckDisposed(); + _designTimeVisible = value; #if !PLATFORM_COMPACTFRAMEWORK TypeDescriptor.Refresh(this); #endif } @@ -637,9 +699,10 @@ /// Clones a command, including all its parameters /// /// A new SQLiteCommand with the same commandtext, connection and parameters public object Clone() { + CheckDisposed(); return new SQLiteCommand(this); } } } Index: System.Data.SQLite/SQLiteCommandBuilder.cs ================================================================== --- System.Data.SQLite/SQLiteCommandBuilder.cs +++ System.Data.SQLite/SQLiteCommandBuilder.cs @@ -32,11 +32,54 @@ public SQLiteCommandBuilder(SQLiteDataAdapter adp) { QuotePrefix = "["; QuoteSuffix = "]"; DataAdapter = adp; - } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteCommandBuilder).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Minimal amount of parameter processing. Primarily sets the DbType for the parameter equal to the provider type in the schema /// /// The parameter to use in applying custom behaviors to a row @@ -103,69 +146,75 @@ /// /// Gets/sets the DataAdapter for this CommandBuilder /// public new SQLiteDataAdapter DataAdapter - { - get { return (SQLiteDataAdapter)base.DataAdapter; } - set { base.DataAdapter = value; } + { + get { CheckDisposed(); return (SQLiteDataAdapter)base.DataAdapter; } + set { CheckDisposed(); base.DataAdapter = value; } } /// /// Returns the automatically-generated SQLite command to delete rows from the database /// /// - public new SQLiteCommand GetDeleteCommand() - { + public new SQLiteCommand GetDeleteCommand() + { + CheckDisposed(); return (SQLiteCommand)base.GetDeleteCommand(); } /// /// Returns the automatically-generated SQLite command to delete rows from the database /// /// /// public new SQLiteCommand GetDeleteCommand(bool useColumnsForParameterNames) - { + { + CheckDisposed(); return (SQLiteCommand)base.GetDeleteCommand(useColumnsForParameterNames); } /// /// Returns the automatically-generated SQLite command to update rows in the database /// /// - public new SQLiteCommand GetUpdateCommand() - { + public new SQLiteCommand GetUpdateCommand() + { + CheckDisposed(); return (SQLiteCommand)base.GetUpdateCommand(); } /// /// Returns the automatically-generated SQLite command to update rows in the database /// /// /// public new SQLiteCommand GetUpdateCommand(bool useColumnsForParameterNames) - { + { + CheckDisposed(); return (SQLiteCommand)base.GetUpdateCommand(useColumnsForParameterNames); } /// /// Returns the automatically-generated SQLite command to insert rows into the database /// /// public new SQLiteCommand GetInsertCommand() - { + { + CheckDisposed(); return (SQLiteCommand)base.GetInsertCommand(); } /// /// Returns the automatically-generated SQLite command to insert rows into the database /// /// /// public new SQLiteCommand GetInsertCommand(bool useColumnsForParameterNames) - { + { + CheckDisposed(); return (SQLiteCommand)base.GetInsertCommand(useColumnsForParameterNames); } /// /// Overridden to hide its property from the designer @@ -174,15 +223,17 @@ [Browsable(false)] #endif public override CatalogLocation CatalogLocation { get - { + { + CheckDisposed(); return base.CatalogLocation; } set - { + { + CheckDisposed(); base.CatalogLocation = value; } } /// @@ -192,15 +243,17 @@ [Browsable(false)] #endif public override string CatalogSeparator { get - { + { + CheckDisposed(); return base.CatalogSeparator; } set - { + { + CheckDisposed(); base.CatalogSeparator = value; } } /// @@ -211,15 +264,17 @@ #endif [DefaultValue("[")] public override string QuotePrefix { get - { + { + CheckDisposed(); return base.QuotePrefix; } set - { + { + CheckDisposed(); base.QuotePrefix = value; } } /// @@ -229,26 +284,30 @@ [Browsable(false)] #endif public override string QuoteSuffix { get - { + { + CheckDisposed(); return base.QuoteSuffix; } set - { + { + CheckDisposed(); base.QuoteSuffix = value; } } /// /// Places brackets around an identifier /// /// The identifier to quote /// The bracketed identifier - public override string QuoteIdentifier(string unquotedIdentifier) - { + public override string QuoteIdentifier(string unquotedIdentifier) + { + CheckDisposed(); + if (String.IsNullOrEmpty(QuotePrefix) || String.IsNullOrEmpty(QuoteSuffix) || String.IsNullOrEmpty(unquotedIdentifier)) return unquotedIdentifier; @@ -258,12 +317,14 @@ /// /// Removes brackets around an identifier /// /// The quoted (bracketed) identifier /// The undecorated identifier - public override string UnquoteIdentifier(string quotedIdentifier) - { + public override string UnquoteIdentifier(string quotedIdentifier) + { + CheckDisposed(); + if (String.IsNullOrEmpty(QuotePrefix) || String.IsNullOrEmpty(QuoteSuffix) || String.IsNullOrEmpty(quotedIdentifier)) return quotedIdentifier; @@ -281,15 +342,17 @@ [Browsable(false)] #endif public override string SchemaSeparator { get - { + { + CheckDisposed(); return base.SchemaSeparator; } - set - { + set + { + CheckDisposed(); base.SchemaSeparator = value; } } /// Index: System.Data.SQLite/SQLiteConnection.cs ================================================================== --- System.Data.SQLite/SQLiteConnection.cs +++ System.Data.SQLite/SQLiteConnection.cs @@ -311,10 +311,55 @@ } } } } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteConnection).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + Close(); + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + #if PLATFORM_COMPACTFRAMEWORK /// /// Obsolete /// public override int ConnectionTimeout @@ -334,22 +379,10 @@ public object Clone() { return new SQLiteConnection(this); } - /// - /// Disposes of the SQLiteConnection, closing it if it is active. - /// - /// True if the connection is being explicitly closed. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - Close(); - } - /// /// Creates a database file. This just creates a zero-byte file which SQLite /// will turn into a database when the file is opened properly. /// /// The file to create Index: System.Data.SQLite/SQLiteDataAdapter.cs ================================================================== --- System.Data.SQLite/SQLiteDataAdapter.cs +++ System.Data.SQLite/SQLiteDataAdapter.cs @@ -62,17 +62,62 @@ { SQLiteConnection cnn = new SQLiteConnection(connectionString); SelectCommand = new SQLiteCommand(commandText, cnn); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteDataAdapter).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Row updating event handler /// public event EventHandler RowUpdating { add { + CheckDisposed(); + #if !PLATFORM_COMPACTFRAMEWORK EventHandler previous = (EventHandler)base.Events[_updatingEventPH]; if ((previous != null) && (value.Target is DbCommandBuilder)) { EventHandler handler = (EventHandler)FindBuilder(previous); @@ -82,11 +127,11 @@ } } #endif base.Events.AddHandler(_updatingEventPH, value); } - remove { base.Events.RemoveHandler(_updatingEventPH, value); } + remove { CheckDisposed(); base.Events.RemoveHandler(_updatingEventPH, value); } } #if !PLATFORM_COMPACTFRAMEWORK internal static Delegate FindBuilder(MulticastDelegate mcd) { @@ -108,12 +153,12 @@ /// /// Row updated event handler /// public event EventHandler RowUpdated { - add { base.Events.AddHandler(_updatedEventPH, value); } - remove { base.Events.RemoveHandler(_updatedEventPH, value); } + add { CheckDisposed(); base.Events.AddHandler(_updatedEventPH, value); } + remove { CheckDisposed(); base.Events.RemoveHandler(_updatedEventPH, value); } } /// /// Raised by the underlying DbDataAdapter when a row is being updated /// @@ -144,12 +189,12 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand SelectCommand { - get { return (SQLiteCommand)base.SelectCommand; } - set { base.SelectCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.SelectCommand; } + set { CheckDisposed(); base.SelectCommand = value; } } /// /// Gets/sets the insert command for this DataAdapter /// @@ -156,12 +201,12 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand InsertCommand { - get { return (SQLiteCommand)base.InsertCommand; } - set { base.InsertCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.InsertCommand; } + set { CheckDisposed(); base.InsertCommand = value; } } /// /// Gets/sets the update command for this DataAdapter /// @@ -168,12 +213,12 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand UpdateCommand { - get { return (SQLiteCommand)base.UpdateCommand; } - set { base.UpdateCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.UpdateCommand; } + set { CheckDisposed(); base.UpdateCommand = value; } } /// /// Gets/sets the delete command for this DataAdapter /// @@ -180,10 +225,10 @@ #if !PLATFORM_COMPACTFRAMEWORK [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public new SQLiteCommand DeleteCommand { - get { return (SQLiteCommand)base.DeleteCommand; } - set { base.DeleteCommand = value; } + get { CheckDisposed(); return (SQLiteCommand)base.DeleteCommand; } + set { CheckDisposed(); base.DeleteCommand = value; } } } } Index: System.Data.SQLite/SQLiteDataReader.cs ================================================================== --- System.Data.SQLite/SQLiteDataReader.cs +++ System.Data.SQLite/SQLiteDataReader.cs @@ -63,15 +63,10 @@ /// /// If set, then raise an exception when the object is accessed after being disposed. /// internal bool _throwOnDisposed; - - /// - /// If set, then the object is currently being disposed. - /// - internal bool _disposing; /// /// An array of rowid's for the active statement if CommandBehavior.KeyInfo is specified /// private SQLiteKeyReader _keyInfo; @@ -104,38 +99,76 @@ _activeStatementIndex = -1; _rowsAffected = -1; if (_command != null) NextResult(); - } - - internal void Cancel() - { - _version = 0; + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed && _throwOnDisposed) + throw new ObjectDisposedException(typeof(SQLiteDataReader).Name); +#endif } + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Dispose of all resources used by this datareader. /// /// protected override void Dispose(bool disposing) { - // - // NOTE: Fix for ticket [e1b2e0f769], do NOT throw exceptions while we - // are being disposed. - // - _disposing = true; - _throwOnDisposed = false; - - base.Dispose(disposing); - } + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + // + // NOTE: Fix for ticket [e1b2e0f769], do NOT throw exceptions + // while we are being disposed. + // + _throwOnDisposed = false; + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + internal void Cancel() + { + _version = 0; + } /// /// Closes the datareader, potentially closing the connection as well if CommandBehavior.CloseConnection was specified. /// public override void Close() - { + { + CheckDisposed(); + try { if (_command != null) { try @@ -214,22 +247,24 @@ /// /// Enumerator support /// /// Returns a DbEnumerator object. - public override Collections.IEnumerator GetEnumerator() - { + public override Collections.IEnumerator GetEnumerator() + { + CheckDisposed(); return new DbEnumerator(this, ((_commandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)); } /// /// Not implemented. Returns 0 /// public override int Depth { get - { + { + CheckDisposed(); CheckClosed(); return 0; } } @@ -237,12 +272,14 @@ /// Returns the number of columns in the current resultset /// public override int FieldCount { get - { + { + CheckDisposed(); CheckClosed(); + if (_keyInfo == null) return _fieldCount; return _fieldCount + _keyInfo.Count; } @@ -251,12 +288,13 @@ /// /// Returns the number of visible fielsd in the current resultset /// public override int VisibleFieldCount { - get - { + get + { + CheckDisposed(); CheckClosed(); return _fieldCount; } } @@ -272,13 +310,14 @@ /// This function throws an InvalidTypeCast() exception if the requested type doesn't match the column's definition or affinity. /// /// The index of the column to type-check /// The type we want to get out of the column private TypeAffinity VerifyType(int i, DbType typ) - { + { CheckClosed(); CheckValidRow(); + TypeAffinity affinity = GetSQLiteType(i).Affinity; switch (affinity) { case TypeAffinity.Int64: @@ -319,12 +358,14 @@ /// /// Retrieves the column as a boolean value /// /// The index of the column to retrieve /// bool - public override bool GetBoolean(int i) - { + public override bool GetBoolean(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetBoolean(i - VisibleFieldCount); VerifyType(i, DbType.Boolean); return Convert.ToBoolean(GetValue(i), CultureInfo.CurrentCulture); @@ -334,11 +375,13 @@ /// Retrieves the column as a single byte value /// /// The index of the column to retrieve /// byte public override byte GetByte(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetByte(i - VisibleFieldCount); VerifyType(i, DbType.Byte); return Convert.ToByte(_activeStatement._sql.GetInt32(_activeStatement, i)); @@ -355,11 +398,13 @@ /// The actual number of bytes written into the array /// /// To determine the number of bytes in the column, pass a null value for the buffer. The total length will be returned. /// public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetBytes(i - VisibleFieldCount, fieldOffset, buffer, bufferoffset, length); VerifyType(i, DbType.Binary); return _activeStatement._sql.GetBytes(_activeStatement, i, (int)fieldOffset, buffer, bufferoffset, length); @@ -369,11 +414,13 @@ /// Returns the column as a single character /// /// The index of the column to retrieve /// char public override char GetChar(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetChar(i - VisibleFieldCount); VerifyType(i, DbType.SByte); return Convert.ToChar(_activeStatement._sql.GetInt32(_activeStatement, i)); @@ -390,11 +437,13 @@ /// The actual number of characters written into the array /// /// To determine the number of characters in the column, pass a null value for the buffer. The total length will be returned. /// public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetChars(i - VisibleFieldCount, fieldoffset, buffer, bufferoffset, length); VerifyType(i, DbType.String); return _activeStatement._sql.GetChars(_activeStatement, i, (int)fieldoffset, buffer, bufferoffset, length); @@ -404,11 +453,13 @@ /// Retrieves the name of the back-end datatype of the column /// /// The index of the column to retrieve /// string public override string GetDataTypeName(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDataTypeName(i - VisibleFieldCount); SQLiteType typ = GetSQLiteType(i); if (typ.Type == DbType.Object) return SQLiteConvert.SQLiteTypeToType(typ).Name; @@ -418,12 +469,14 @@ /// /// Retrieve the column as a date/time value /// /// The index of the column to retrieve /// DateTime - public override DateTime GetDateTime(int i) - { + public override DateTime GetDateTime(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDateTime(i - VisibleFieldCount); VerifyType(i, DbType.DateTime); return _activeStatement._sql.GetDateTime(_activeStatement, i); @@ -433,11 +486,13 @@ /// Retrieve the column as a decimal value /// /// The index of the column to retrieve /// decimal public override decimal GetDecimal(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDecimal(i - VisibleFieldCount); VerifyType(i, DbType.Decimal); return Decimal.Parse(_activeStatement._sql.GetText(_activeStatement, i), NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture); @@ -447,11 +502,13 @@ /// Returns the column as a double /// /// The index of the column to retrieve /// double public override double GetDouble(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetDouble(i - VisibleFieldCount); VerifyType(i, DbType.Double); return _activeStatement._sql.GetDouble(_activeStatement, i); @@ -461,11 +518,13 @@ /// Returns the .NET type of a given column /// /// The index of the column to retrieve /// Type public override Type GetFieldType(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetFieldType(i - VisibleFieldCount); return SQLiteConvert.SQLiteTypeToType(GetSQLiteType(i)); } @@ -474,11 +533,13 @@ /// Returns a column as a float value /// /// The index of the column to retrieve /// float public override float GetFloat(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetFloat(i - VisibleFieldCount); VerifyType(i, DbType.Single); return Convert.ToSingle(_activeStatement._sql.GetDouble(_activeStatement, i)); @@ -487,12 +548,14 @@ /// /// Returns the column as a Guid /// /// The index of the column to retrieve /// Guid - public override Guid GetGuid(int i) - { + public override Guid GetGuid(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetGuid(i - VisibleFieldCount); TypeAffinity affinity = VerifyType(i, DbType.Guid); if (affinity == TypeAffinity.Blob) @@ -508,12 +571,14 @@ /// /// Returns the column as a short /// /// The index of the column to retrieve /// Int16 - public override Int16 GetInt16(int i) - { + public override Int16 GetInt16(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetInt16(i - VisibleFieldCount); VerifyType(i, DbType.Int16); return Convert.ToInt16(_activeStatement._sql.GetInt32(_activeStatement, i)); @@ -522,12 +587,14 @@ /// /// Retrieves the column as an int /// /// The index of the column to retrieve /// Int32 - public override Int32 GetInt32(int i) - { + public override Int32 GetInt32(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetInt32(i - VisibleFieldCount); VerifyType(i, DbType.Int32); return _activeStatement._sql.GetInt32(_activeStatement, i); @@ -536,12 +603,14 @@ /// /// Retrieves the column as a long /// /// The index of the column to retrieve /// Int64 - public override Int64 GetInt64(int i) - { + public override Int64 GetInt64(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetInt64(i - VisibleFieldCount); VerifyType(i, DbType.Int64); return _activeStatement._sql.GetInt64(_activeStatement, i); @@ -551,11 +620,13 @@ /// Retrieves the name of the column /// /// The index of the column to retrieve /// string public override string GetName(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetName(i - VisibleFieldCount); return _activeStatement._sql.ColumnName(_activeStatement, i); } @@ -564,12 +635,14 @@ /// Retrieves the i of a column, given its name /// /// The name of the column to retrieve /// The int i of the column public override int GetOrdinal(string name) - { + { + CheckDisposed(); CheckClosed(); + int r = _activeStatement._sql.ColumnIndex(_activeStatement, name); if (r == -1 && _keyInfo != null) { r = _keyInfo.GetOrdinal(name); if (r > -1) r += VisibleFieldCount; @@ -582,11 +655,12 @@ /// Schema information in SQLite is difficult to map into .NET conventions, so a lot of work must be done /// to gather the necessary information so it can be represented in an ADO.NET manner. /// /// Returns a DataTable containing the schema information for the active SELECT statement being processed. public override DataTable GetSchemaTable() - { + { + CheckDisposed(); return GetSchemaTable(true, false); } private class ColumnParent : IEqualityComparer { @@ -942,11 +1016,13 @@ /// Retrieves the column as a string /// /// The index of the column to retrieve /// string public override string GetString(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetString(i - VisibleFieldCount); VerifyType(i, DbType.String); return _activeStatement._sql.GetText(_activeStatement, i); @@ -956,11 +1032,13 @@ /// Retrieves the column as an object corresponding to the underlying datatype of the column /// /// The index of the column to retrieve /// object public override object GetValue(int i) - { + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetValue(i - VisibleFieldCount); SQLiteType typ = GetSQLiteType(i); @@ -970,12 +1048,14 @@ /// /// Retreives the values of multiple columns, up to the size of the supplied array /// /// The array to fill with values from the columns in the current resultset /// The number of columns retrieved - public override int GetValues(object[] values) - { + public override int GetValues(object[] values) + { + CheckDisposed(); + int nMax = FieldCount; if (values.Length < nMax) nMax = values.Length; for (int n = 0; n < nMax; n++) { @@ -988,32 +1068,35 @@ /// /// Returns True if the resultset has rows that can be fetched /// public override bool HasRows { - get - { + get + { + CheckDisposed(); CheckClosed(); return (_readingState != 1); } } /// /// Returns True if the data reader is closed /// public override bool IsClosed - { - get { return (_command == null); } + { + get { CheckDisposed(); return (_command == null); } } /// /// Returns True if the specified column is null /// /// The index of the column to retrieve /// True or False - public override bool IsDBNull(int i) - { + public override bool IsDBNull(int i) + { + CheckDisposed(); + if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.IsDBNull(i - VisibleFieldCount); return _activeStatement._sql.IsNull(_activeStatement, i); } @@ -1021,11 +1104,12 @@ /// /// Moves to the next resultset in multiple row-returning SQL command. /// /// True if the command was successful and a new resultset is available, False otherwise. public override bool NextResult() - { + { + CheckDisposed(); CheckClosed(); SQLiteStatement stmt = null; int fieldCount; @@ -1136,11 +1220,12 @@ /// /// Reads the next row from the resultset /// /// True if a new row was successfully loaded and is ready for processing public override bool Read() - { + { + CheckDisposed(); CheckClosed(); if (_readingState == -1) // First step was already done at the NextResult() level, so don't step again, just return true. { _readingState = 0; @@ -1168,32 +1253,32 @@ /// /// Retrieve the count of records affected by an update/insert command. Only valid once the data reader is closed! /// public override int RecordsAffected - { - get { return (_rowsAffected < 0) ? 0 : _rowsAffected; } + { + get { CheckDisposed(); return (_rowsAffected < 0) ? 0 : _rowsAffected; } } /// /// Indexer to retrieve data from a column given its name /// /// The name of the column to retrieve data for /// The value contained in the column public override object this[string name] - { - get { return GetValue(GetOrdinal(name)); } + { + get { CheckDisposed(); return GetValue(GetOrdinal(name)); } } /// /// Indexer to retrieve data from a column given its i /// /// The index of the column to retrieve /// The value contained in the column public override object this[int i] - { - get { return GetValue(i); } + { + get { CheckDisposed(); return GetValue(i); } } private void LoadKeyInfo() { if (_keyInfo != null) Index: System.Data.SQLite/SQLiteEnlistment.cs ================================================================== --- System.Data.SQLite/SQLiteEnlistment.cs +++ System.Data.SQLite/SQLiteEnlistment.cs @@ -8,11 +8,11 @@ #if !PLATFORM_COMPACTFRAMEWORK namespace System.Data.SQLite { using System.Transactions; - internal class SQLiteEnlistment : IEnlistmentNotification + internal class SQLiteEnlistment : IEnlistmentNotification, IDisposable { internal SQLiteTransaction _transaction; internal Transaction _scope; internal bool _disposeConnection; @@ -22,10 +22,77 @@ _scope = scope; _scope.EnlistVolatile(this, System.Transactions.EnlistmentOptions.None); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteEnlistment).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (_transaction != null) + { + _transaction.Dispose(); + _transaction = null; + } + + if (_scope != null) + { + // _scope.Dispose(); // NOTE: Not "owned" by us. + _scope = null; + } + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteEnlistment() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + private void Cleanup(SQLiteConnection cnn) { if (_disposeConnection) cnn.Dispose(); @@ -34,11 +101,13 @@ } #region IEnlistmentNotification Members public void Commit(Enlistment enlistment) - { + { + CheckDisposed(); + SQLiteConnection cnn = _transaction.Connection; cnn._enlistment = null; try { @@ -52,25 +121,30 @@ { Cleanup(cnn); } } - public void InDoubt(Enlistment enlistment) - { + public void InDoubt(Enlistment enlistment) + { + CheckDisposed(); enlistment.Done(); } - public void Prepare(PreparingEnlistment preparingEnlistment) - { + public void Prepare(PreparingEnlistment preparingEnlistment) + { + CheckDisposed(); + if (_transaction.IsValid(false) == false) preparingEnlistment.ForceRollback(); else preparingEnlistment.Prepared(); } public void Rollback(Enlistment enlistment) - { + { + CheckDisposed(); + SQLiteConnection cnn = _transaction.Connection; cnn._enlistment = null; try { Index: System.Data.SQLite/SQLiteFactory.cs ================================================================== --- System.Data.SQLite/SQLiteFactory.cs +++ System.Data.SQLite/SQLiteFactory.cs @@ -12,24 +12,12 @@ #if !PLATFORM_COMPACTFRAMEWORK /// /// SQLite implementation of DbProviderFactory. /// - public sealed partial class SQLiteFactory : DbProviderFactory - { - /// - /// This event is raised whenever SQLite raises a logging event. - /// Note that this should be set as one of the first things in the - /// application. This event is provided for backward compatibility only. - /// New code should use the SQLiteLog class instead. - /// - public event SQLiteLogEventHandler Log - { - add { SQLiteLog.Log += value; } - remove { SQLiteLog.Log -= value; } - } - + public sealed partial class SQLiteFactory : DbProviderFactory, IDisposable + { /// /// Constructs a new SQLiteFactory object /// /// /// Default constructor @@ -39,10 +27,77 @@ // // NOTE: Do nothing here now. All the logging setup related code has // been moved to the new SQLiteLog static class. // } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteFactory).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteFactory() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// This event is raised whenever SQLite raises a logging event. + /// Note that this should be set as one of the first things in the + /// application. This event is provided for backward compatibility only. + /// New code should use the SQLiteLog class instead. + /// + public event SQLiteLogEventHandler Log + { + add { CheckDisposed(); SQLiteLog.Log += value; } + remove { CheckDisposed(); SQLiteLog.Log -= value; } + } /// /// Static instance member which returns an instanced SQLiteFactory class. /// public static readonly SQLiteFactory Instance = new SQLiteFactory(); @@ -51,55 +106,61 @@ /// Returns a new SQLiteCommand object. /// /// A SQLiteCommand object. public override DbCommand CreateCommand() { + CheckDisposed(); return new SQLiteCommand(); } /// /// Returns a new SQLiteCommandBuilder object. /// /// A SQLiteCommandBuilder object. public override DbCommandBuilder CreateCommandBuilder() { + CheckDisposed(); return new SQLiteCommandBuilder(); } /// /// Creates a new SQLiteConnection. /// /// A SQLiteConnection object. public override DbConnection CreateConnection() { + CheckDisposed(); return new SQLiteConnection(); } /// /// Creates a new SQLiteConnectionStringBuilder. /// /// A SQLiteConnectionStringBuilder object. public override DbConnectionStringBuilder CreateConnectionStringBuilder() { + CheckDisposed(); return new SQLiteConnectionStringBuilder(); } /// /// Creates a new SQLiteDataAdapter. /// /// A SQLiteDataAdapter object. public override DbDataAdapter CreateDataAdapter() { + CheckDisposed(); return new SQLiteDataAdapter(); } /// /// Creates a new SQLiteParameter. /// /// A SQLiteParameter object. public override DbParameter CreateParameter() { + CheckDisposed(); return new SQLiteParameter(); } } #endif } Index: System.Data.SQLite/SQLiteFunction.cs ================================================================== --- System.Data.SQLite/SQLiteFunction.cs +++ System.Data.SQLite/SQLiteFunction.cs @@ -79,20 +79,101 @@ /// Internal constructor, initializes the function's internal variables. /// protected SQLiteFunction() { _contextDataList = new Dictionary(); - } - + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + /// + /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if + /// someone closes the connection while a DataReader is open. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteFunction).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Placeholder for a user-defined disposal routine + /// + /// True if the object is being disposed explicitly + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + IDisposable disp; + + foreach (KeyValuePair kv in _contextDataList) + { + disp = kv.Value._data as IDisposable; + if (disp != null) + disp.Dispose(); + } + _contextDataList.Clear(); + + _InvokeFunc = null; + _StepFunc = null; + _FinalFunc = null; + _CompareFunc = null; + _base = null; + _contextDataList = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteFunction() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Returns a reference to the underlying connection's SQLiteConvert class, which can be used to convert /// strings and DateTime's into the current connection's encoding schema. /// public SQLiteConvert SQLiteConvert { get - { + { + CheckDisposed(); return _base; } } /// @@ -105,12 +186,13 @@ /// /// The arguments for the command to process /// You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error, /// just return it! - public virtual object Invoke(object[] args) - { + public virtual object Invoke(object[] args) + { + CheckDisposed(); return null; } /// /// Aggregate functions override this method to do their magic. @@ -119,12 +201,13 @@ /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible. /// /// The arguments for the command to process /// The 1-based step number. This is incrememted each time the step method is called. /// A placeholder for implementers to store contextual data pertaining to the current context. - public virtual void Step(object[] args, int stepNumber, ref object contextData) - { + public virtual void Step(object[] args, int stepNumber, ref object contextData) + { + CheckDisposed(); } /// /// Aggregate functions override this method to finish their aggregate processing. /// @@ -140,22 +223,24 @@ /// You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error, /// just return it! /// public virtual object Final(object contextData) - { + { + CheckDisposed(); return null; } /// /// User-defined collation sequences override this method to provide a custom string sorting algorithm. /// /// The first string to compare /// The second strnig to compare /// 1 if param1 is greater than param2, 0 if they are equal, or -1 if param1 is less than param2 - public virtual int Compare(string param1, string param2) - { + public virtual int Compare(string param1, string param2) + { + CheckDisposed(); return 0; } /// /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to. @@ -216,11 +301,11 @@ /// /// Takes the return value from Invoke() and Final() and figures out how to return it to SQLite's context. /// /// The context the return value applies to /// The parameter to return to SQLite - void SetReturnValue(IntPtr context, object returnValue) + private void SetReturnValue(IntPtr context, object returnValue) { if (returnValue == null || returnValue == DBNull.Value) { _base.ReturnNull(context); return; @@ -349,47 +434,10 @@ IDisposable disp = obj as IDisposable; if (disp != null) disp.Dispose(); } - /// - /// Placeholder for a user-defined disposal routine - /// - /// True if the object is being disposed explicitly - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - IDisposable disp; - - foreach (KeyValuePair kv in _contextDataList) - { - disp = kv.Value._data as IDisposable; - if (disp != null) - disp.Dispose(); - } - _contextDataList.Clear(); - - _InvokeFunc = null; - _StepFunc = null; - _FinalFunc = null; - _CompareFunc = null; - _base = null; - _contextDataList = null; - } - } - - /// - /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if - /// someone closes the connection while a DataReader is open. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - /// /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that /// have a SQLiteFunctionAttribute attribute, and registering them accordingly. /// #if !PLATFORM_COMPACTFRAMEWORK @@ -537,11 +585,52 @@ /// /// protected CollationSequence GetCollationSequence() { return _base.GetCollationSequence(this, _context); - } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteFunctionEx).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + //if (disposing) + //{ + // //////////////////////////////////// + // // dispose managed resources here... + // //////////////////////////////////// + //} + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion } /// /// The type of user-defined function to declare /// Index: System.Data.SQLite/SQLiteKeyReader.cs ================================================================== --- System.Data.SQLite/SQLiteKeyReader.cs +++ System.Data.SQLite/SQLiteKeyReader.cs @@ -40,58 +40,108 @@ internal int column; } /// /// A single sub-query for a given table/database. - /// - private sealed class KeyQuery : IDisposable - { - private SQLiteCommand _command; - internal SQLiteDataReader _reader; - - internal KeyQuery(SQLiteConnection cnn, string database, string table, params string[] columns) - { - using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder()) - { - _command = cnn.CreateCommand(); - for (int n = 0; n < columns.Length; n++) - { - columns[n] = builder.QuoteIdentifier(columns[n]); - } - } - _command.CommandText = String.Format(CultureInfo.InvariantCulture, "SELECT {0} FROM [{1}].[{2}] WHERE ROWID = ?", String.Join(",", columns), database, table); - _command.Parameters.AddWithValue(null, (long)0); - } - - internal bool IsValid - { - set - { - if (value != false) throw new ArgumentException(); - if (_reader != null) - { - _reader.Dispose(); - _reader = null; - } - } - } - - internal void Sync(long rowid) - { - IsValid = false; - _command.Parameters[0].Value = rowid; - _reader = _command.ExecuteReader(); - _reader.Read(); - } - - public void Dispose() - { - IsValid = false; - - if (_command != null) _command.Dispose(); - _command = null; - } + /// + private sealed class KeyQuery : IDisposable + { + private SQLiteCommand _command; + internal SQLiteDataReader _reader; + + internal KeyQuery(SQLiteConnection cnn, string database, string table, params string[] columns) + { + using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder()) + { + _command = cnn.CreateCommand(); + for (int n = 0; n < columns.Length; n++) + { + columns[n] = builder.QuoteIdentifier(columns[n]); + } + } + _command.CommandText = String.Format(CultureInfo.InvariantCulture, "SELECT {0} FROM [{1}].[{2}] WHERE ROWID = ?", String.Join(",", columns), database, table); + _command.Parameters.AddWithValue(null, (long)0); + } + + internal bool IsValid + { + set + { + if (value != false) throw new ArgumentException(); + if (_reader != null) + { + _reader.Dispose(); + _reader = null; + } + } + } + + internal void Sync(long rowid) + { + IsValid = false; + _command.Parameters[0].Value = rowid; + _reader = _command.ExecuteReader(); + _reader.Read(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(KeyQuery).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + IsValid = false; + + if (_command != null) _command.Dispose(); + _command = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~KeyQuery() + { + Dispose(false); + } + #endregion } /// /// This function does all the nasty work at determining what keys need to be returned for /// a given statement. @@ -257,10 +307,77 @@ // CommandBehavior.KeyInfo _keyInfo = new KeyInfo[keys.Count]; keys.CopyTo(_keyInfo); } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteKeyReader).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + _stmt = null; + + if (_keyInfo == null) return; + + for (int n = 0; n < _keyInfo.Length; n++) + { + if (_keyInfo[n].query != null) + _keyInfo[n].query.Dispose(); + } + + _keyInfo = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteKeyReader() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// How many additional columns of keyinfo we're holding /// internal int Count { @@ -312,24 +429,10 @@ if (_keyInfo[n].query != null) _keyInfo[n].query.IsValid = false; } } - public void Dispose() - { - _stmt = null; - - if (_keyInfo == null) return; - - for (int n = 0; n < _keyInfo.Length; n++) - { - if (_keyInfo[n].query != null) - _keyInfo[n].query.Dispose(); - } - _keyInfo = null; - } - internal string GetDataTypeName(int i) { Sync(); if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetDataTypeName(_keyInfo[i].column); else return "integer"; Index: System.Data.SQLite/SQLiteStatement.cs ================================================================== --- System.Data.SQLite/SQLiteStatement.cs +++ System.Data.SQLite/SQLiteStatement.cs @@ -86,10 +86,79 @@ _paramValues[x] = null; } } } + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable Members + /// + /// Disposes and finalizes the statement + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteStatement).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (_sqlite_stmt != null) + { + _sqlite_stmt.Dispose(); + _sqlite_stmt = null; + } + + _paramNames = null; + _paramValues = null; + _sql = null; + _sqlStatement = null; + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region Destructor + ~SQLiteStatement() + { + Dispose(false); + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// /// Called by SQLiteParameterCollection, this function determines if the specified parameter name belongs to /// this statement, and if so, keeps a reference to the parameter so it can be bound later. /// /// The parameter name to map @@ -115,29 +184,10 @@ } } return false; } - #region IDisposable Members - /// - /// Disposes and finalizes the statement - /// - public void Dispose() - { - if (_sqlite_stmt != null) - { - _sqlite_stmt.Dispose(); - } - _sqlite_stmt = null; - - _paramNames = null; - _paramValues = null; - _sql = null; - _sqlStatement = null; - } - #endregion - /// /// Bind all parameters, making sure the caller didn't miss any /// internal void BindParameters() { Index: System.Data.SQLite/SQLiteTransaction.cs ================================================================== --- System.Data.SQLite/SQLiteTransaction.cs +++ System.Data.SQLite/SQLiteTransaction.cs @@ -55,17 +55,69 @@ _cnn._transactionLevel--; _cnn = null; throw; } } - } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + #region IDisposable "Pattern" Members + private bool disposed; + private void CheckDisposed() /* throw */ + { +#if THROW_ON_DISPOSED + if (disposed) + throw new ObjectDisposedException(typeof(SQLiteTransaction).Name); +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /// + /// Disposes the transaction. If it is currently active, any changes are rolled back. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!disposed) + { + if (disposing) + { + //////////////////////////////////// + // dispose managed resources here... + //////////////////////////////////// + + if (IsValid(false)) + { + IssueRollback(); + } + } + + ////////////////////////////////////// + // release unmanaged resources here... + ////////////////////////////////////// + + disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + #endregion + + /////////////////////////////////////////////////////////////////////////////////////////////// /// /// Commits the current transaction. /// public override void Commit() - { + { + CheckDisposed(); IsValid(true); if (_cnn._transactionLevel - 1 == 0) { using (SQLiteCommand cmd = _cnn.CreateCommand()) @@ -80,50 +132,36 @@ /// /// Returns the underlying connection to which this transaction applies. /// public new SQLiteConnection Connection - { - get { return _cnn; } + { + get { CheckDisposed(); return _cnn; } } /// /// Forwards to the local Connection property /// protected override DbConnection DbConnection - { + { get { return Connection; } } - /// - /// Disposes the transaction. If it is currently active, any changes are rolled back. - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (IsValid(false)) - { - IssueRollback(); - } - } - base.Dispose(disposing); - } - /// /// Gets the isolation level of the transaction. SQLite only supports Serializable transactions. /// public override IsolationLevel IsolationLevel - { - get { return _level; } + { + get { CheckDisposed(); return _level; } } /// /// Rolls back the active transaction. /// public override void Rollback() - { + { + CheckDisposed(); IsValid(true); IssueRollback(); } internal void IssueRollback() Index: System.Data.SQLite/SR.Designer.cs ================================================================== --- System.Data.SQLite/SR.Designer.cs +++ System.Data.SQLite/SR.Designer.cs @@ -1,5 +1,12 @@ +/******************************************************** + * ADO.NET 2.0 Data Provider for SQLite Version 3.X + * Written by Robert Simpson (robert@blackcastlesoft.com) + * + * Released to the public domain, use at your own risk! + ********************************************************/ + //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // Index: System.Data.SQLite/System.Data.SQLite.Properties.targets ================================================================== --- System.Data.SQLite/System.Data.SQLite.Properties.targets +++ System.Data.SQLite/System.Data.SQLite.Properties.targets @@ -48,6 +48,13 @@ flag from the AssemblyNameFlags enumeration? --> $(DefineConstants);RETARGETABLE + + + + $(DefineConstants);THROW_ON_DISPOSED + Index: testlinq/NorthwindModel2010.Designer.cs ================================================================== --- testlinq/NorthwindModel2010.Designer.cs +++ testlinq/NorthwindModel2010.Designer.cs @@ -1,13 +1,11 @@ - //------------------------------------------------------------------------------ // // This code was generated from a template. // Index: www/test.wiki ================================================================== --- www/test.wiki +++ www/test.wiki @@ -109,11 +109,11 @@
  • Make sure all tests pass; the log file "%TEMP%\EagleShell.exe.test.<pid>.log" may be checked if any errors should occur. EagleTest should produce "success" messages very similar to the following:

    - PASSED: 96
    - TOTAL: 96
    + PASSED: 107
    + TOTAL: 107
    PASS PERCENTAGE: 100%
    OVERALL RESULT: SUCCESS