SQLite.SQLiteConnection源代码

摘自编译后的.cs文件,版权归原作者所有:

// Decompiled with JetBrains decompiler
// Type: SQLite.SQLiteConnection
// Assembly: SQLite-net, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E6176266-E10E-4143-A13A-15E20E5B743C
// Assembly location: ...\packages\sqlite-net-pcl.1.4.118\lib\netstandard1.1\SQLite-net.dll

using SQLitePCL;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;

namespace SQLite
{
  /// <summary>Represents an open connection to a SQLite database.</summary>
  public class SQLiteConnection : IDisposable
  {
    private Random _rand = new Random();
    private bool _open;
    private TimeSpan _busyTimeout;
    private Dictionary<string, TableMapping> _mappings;
    private Dictionary<string, TableMapping> _tables;
    private Stopwatch _sw;
    private long _elapsedMilliseconds;
    private int _transactionDepth;
    internal static readonly sqlite3 NullHandle;

    public sqlite3 Handle { get; private set; }

    public string DatabasePath { get; private set; }

    public bool TimeExecution { get; set; }

    public bool Trace { get; set; }

    public Action<string> Tracer { get; set; }

    public bool StoreDateTimeAsTicks { get; private set; }

    static SQLiteConnection()
    {
      Batteries_V2.Init();
    }

    /// <summary>
    /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
    /// </summary>
    /// <param name="databasePath">
    /// Specifies the path to the database file.
    /// </param>
    /// <param name="storeDateTimeAsTicks">
    /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
    /// absolutely do want to store them as Ticks in all new projects. The value of false is
    /// only here for backwards compatibility. There is a *significant* speed advantage, with no
    /// down sides, when setting storeDateTimeAsTicks = true.
    /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
    /// the storeDateTimeAsTicks parameter.
    /// </param>
    public SQLiteConnection(string databasePath, bool storeDateTimeAsTicks = true)
      : this(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
    {
    }

    /// <summary>
    /// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
    /// </summary>
    /// <param name="databasePath">
    /// Specifies the path to the database file.
    /// </param>
    /// <param name="openFlags">
    /// Flags controlling how the connection should be opened.
    /// </param>
    /// <param name="storeDateTimeAsTicks">
    /// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
    /// absolutely do want to store them as Ticks in all new projects. The value of false is
    /// only here for backwards compatibility. There is a *significant* speed advantage, with no
    /// down sides, when setting storeDateTimeAsTicks = true.
    /// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
    /// the storeDateTimeAsTicks parameter.
    /// </param>
    public SQLiteConnection(string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
    {
      if (string.IsNullOrEmpty(databasePath))
        throw new ArgumentException("Must be specified", nameof (databasePath));
      this.DatabasePath = databasePath;
      sqlite3 db;
      SQLite3.Result r = SQLite3.Open(databasePath, out db, (int) openFlags, IntPtr.Zero);
      this.Handle = db;
      if (r != SQLite3.Result.OK)
        throw SQLiteException.New(r, string.Format("Could not open database file: {0} ({1})", new object[2]
        {
          (object) this.DatabasePath,
          (object) r
        }));
      this._open = true;
      this.StoreDateTimeAsTicks = storeDateTimeAsTicks;
      this.BusyTimeout = TimeSpan.FromSeconds(0.1);
      this.Tracer = (Action<string>) (line => {});
    }

    /// <summary>
    /// Sets a busy handler to sleep the specified amount of time when a table is locked.
    /// The handler will sleep multiple times until a total time of <see cref="P:SQLite.SQLiteConnection.BusyTimeout" /> has accumulated.
    /// </summary>
    public TimeSpan BusyTimeout
    {
      get
      {
        return this._busyTimeout;
      }
      set
      {
        this._busyTimeout = value;
        if (this.Handle == SQLiteConnection.NullHandle)
          return;
        int num = (int) SQLite3.BusyTimeout(this.Handle, (int) this._busyTimeout.TotalMilliseconds);
      }
    }

    /// <summary>
    /// Returns the mappings from types to tables that the connection
    /// currently understands.
    /// </summary>
    public IEnumerable<TableMapping> TableMappings
    {
      get
      {
        if (this._tables == null)
          return Enumerable.Empty<TableMapping>();
        return (IEnumerable<TableMapping>) this._tables.Values;
      }
    }

    /// <summary>
    /// Retrieves the mapping that is automatically generated for the given type.
    /// </summary>
    /// <param name="type">
    /// The type whose mapping to the database is returned.
    /// </param>
    /// <param name="createFlags">
    /// Optional flags allowing implicit PK and indexes based on naming conventions
    /// </param>
    /// <returns>
    /// The mapping represents the schema of the columns of the database and contains
    /// methods to set and get properties of objects.
    /// </returns>
    public TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None)
    {
      if (this._mappings == null)
        this._mappings = new Dictionary<string, TableMapping>();
      TableMapping tableMapping;
      if (!this._mappings.TryGetValue(type.FullName, out tableMapping))
      {
        tableMapping = new TableMapping(type, createFlags);
        this._mappings[type.FullName] = tableMapping;
      }
      return tableMapping;
    }

    /// <summary>
    /// Retrieves the mapping that is automatically generated for the given type.
    /// </summary>
    /// <param name="createFlags">
    /// Optional flags allowing implicit PK and indexes based on naming conventions
    /// </param>
    /// <returns>
    /// The mapping represents the schema of the columns of the database and contains
    /// methods to set and get properties of objects.
    /// </returns>
    public TableMapping GetMapping<T>(CreateFlags createFlags = CreateFlags.None)
    {
      return this.GetMapping(typeof (T), createFlags);
    }

    /// <summary>
    /// Executes a "drop table" on the database.  This is non-recoverable.
    /// </summary>
    public int DropTable<T>()
    {
      return this.DropTable(this.GetMapping(typeof (T), CreateFlags.None));
    }

    /// <summary>
    /// Executes a "drop table" on the database.  This is non-recoverable.
    /// </summary>
    /// <param name="map">The TableMapping used to identify the table.</param>
    public int DropTable(TableMapping map)
    {
      return this.Execute(string.Format("drop table if exists \"{0}\"", new object[1]
      {
        (object) map.TableName
      }));
    }

    /// <summary>
    /// Executes a "create table if not exists" on the database. It also
    /// creates any specified indexes on the columns of the table. It uses
    /// a schema automatically generated from the specified type. You can
    /// later access this schema by calling GetMapping.
    /// </summary>
    /// <returns>The number of entries added to the database schema.</returns>
    public int CreateTable<T>(CreateFlags createFlags = CreateFlags.None)
    {
      return this.CreateTable(typeof (T), createFlags);
    }

    /// <summary>
    /// Executes a "create table if not exists" on the database. It also
    /// creates any specified indexes on the columns of the table. It uses
    /// a schema automatically generated from the specified type. You can
    /// later access this schema by calling GetMapping.
    /// </summary>
    /// <param name="ty">Type to reflect to a database table.</param>
    /// <param name="createFlags">Optional flags allowing implicit PK and indexes based on naming conventions.</param>
    /// <returns>The number of entries added to the database schema.</returns>
    public int CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None)
    {
      if (this._tables == null)
        this._tables = new Dictionary<string, TableMapping>();
      TableMapping mapping;
      if (!this._tables.TryGetValue(ty.FullName, out mapping))
      {
        mapping = this.GetMapping(ty, createFlags);
        this._tables.Add(ty.FullName, mapping);
      }
      if (mapping.Columns.Length == 0)
        throw new Exception(string.Format("Cannot create a table with zero columns (does '{0}' have public properties?)", new object[1]
        {
          (object) ty.FullName
        }));
      bool flag1 = (uint) (createFlags & CreateFlags.FullTextSearch3) > 0U;
      bool flag2 = (uint) (createFlags & CreateFlags.FullTextSearch4) > 0U;
      string str1 = flag1 | flag2 ? "virtual " : string.Empty;
      string str2 = flag1 ? "using fts3 " : (flag2 ? "using fts4 " : string.Empty);
      int num = this.Execute("create " + str1 + "table if not exists \"" + mapping.TableName + "\" " + str2 + "(\n" + string.Join(",\n", ((IEnumerable<TableMapping.Column>) mapping.Columns).Select<TableMapping.Column, string>((Func<TableMapping.Column, string>) (p => Orm.SqlDecl(p, this.StoreDateTimeAsTicks))).ToArray<string>()) + ")");
      if (num == 0)
        this.MigrateTable(mapping);
      Dictionary<string, SQLiteConnection.IndexInfo> dictionary = new Dictionary<string, SQLiteConnection.IndexInfo>();
      foreach (TableMapping.Column column in mapping.Columns)
      {
        foreach (IndexedAttribute index in column.Indices)
        {
          string key = index.Name ?? mapping.TableName + "_" + column.Name;
          SQLiteConnection.IndexInfo indexInfo;
          if (!dictionary.TryGetValue(key, out indexInfo))
          {
            indexInfo = new SQLiteConnection.IndexInfo()
            {
              IndexName = key,
              TableName = mapping.TableName,
              Unique = index.Unique,
              Columns = new List<SQLiteConnection.IndexedColumn>()
            };
            dictionary.Add(key, indexInfo);
          }
          if (index.Unique != indexInfo.Unique)
            throw new Exception("All the columns in an index must have the same value for their Unique property");
          indexInfo.Columns.Add(new SQLiteConnection.IndexedColumn()
          {
            Order = index.Order,
            ColumnName = column.Name
          });
        }
      }
      foreach (string key in dictionary.Keys)
      {
        SQLiteConnection.IndexInfo indexInfo = dictionary[key];
        string[] array = indexInfo.Columns.OrderBy<SQLiteConnection.IndexedColumn, int>((Func<SQLiteConnection.IndexedColumn, int>) (i => i.Order)).Select<SQLiteConnection.IndexedColumn, string>((Func<SQLiteConnection.IndexedColumn, string>) (i => i.ColumnName)).ToArray<string>();
        num += this.CreateIndex(key, indexInfo.TableName, array, indexInfo.Unique);
      }
      return num;
    }

    /// <summary>Creates an index for the specified table and columns.</summary>
    /// <param name="indexName">Name of the index to create</param>
    /// <param name="tableName">Name of the database table</param>
    /// <param name="columnNames">An array of column names to index</param>
    /// <param name="unique">Whether the index should be unique</param>
    public int CreateIndex(string indexName, string tableName, string[] columnNames, bool unique = false)
    {
      return this.Execute(string.Format("create {2} index if not exists \"{3}\" on \"{0}\"(\"{1}\")", (object) tableName, (object) string.Join("\", \"", columnNames), unique ? (object) nameof (unique) : (object) "", (object) indexName));
    }

    /// <summary>Creates an index for the specified table and column.</summary>
    /// <param name="indexName">Name of the index to create</param>
    /// <param name="tableName">Name of the database table</param>
    /// <param name="columnName">Name of the column to index</param>
    /// <param name="unique">Whether the index should be unique</param>
    public int CreateIndex(string indexName, string tableName, string columnName, bool unique = false)
    {
      return this.CreateIndex(indexName, tableName, new string[1]
      {
        columnName
      }, (unique ? 1 : 0) != 0);
    }

    /// <summary>Creates an index for the specified table and column.</summary>
    /// <param name="tableName">Name of the database table</param>
    /// <param name="columnName">Name of the column to index</param>
    /// <param name="unique">Whether the index should be unique</param>
    public int CreateIndex(string tableName, string columnName, bool unique = false)
    {
      return this.CreateIndex(tableName + "_" + columnName, tableName, columnName, unique);
    }

    /// <summary>Creates an index for the specified table and columns.</summary>
    /// <param name="tableName">Name of the database table</param>
    /// <param name="columnNames">An array of column names to index</param>
    /// <param name="unique">Whether the index should be unique</param>
    public int CreateIndex(string tableName, string[] columnNames, bool unique = false)
    {
      return this.CreateIndex(tableName + "_" + string.Join("_", columnNames), tableName, columnNames, unique);
    }

    /// <summary>
    /// Creates an index for the specified object property.
    /// e.g. CreateIndex<Client>(c => c.Name);
    /// </summary>
    /// <typeparam name="T">Type to reflect to a database table.</typeparam>
    /// <param name="property">Property to index</param>
    /// <param name="unique">Whether the index should be unique</param>
    public void CreateIndex<T>(Expression<Func<T, object>> property, bool unique = false)
    {
      PropertyInfo member = (property.Body.NodeType != ExpressionType.Convert ? property.Body as MemberExpression : ((UnaryExpression) property.Body).Operand as MemberExpression).Member as PropertyInfo;
      if ((object) member == null)
        throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
      string name1 = member.Name;
      TableMapping mapping = this.GetMapping<T>(CreateFlags.None);
      string name2 = mapping.FindColumnWithPropertyName(name1).Name;
      this.CreateIndex(mapping.TableName, name2, unique);
    }

    public List<SQLiteConnection.ColumnInfo> GetTableInfo(string tableName)
    {
      return this.Query<SQLiteConnection.ColumnInfo>("pragma table_info(\"" + tableName + "\")");
    }

    private void MigrateTable(TableMapping map)
    {
      List<SQLiteConnection.ColumnInfo> tableInfo = this.GetTableInfo(map.TableName);
      List<TableMapping.Column> columnList = new List<TableMapping.Column>();
      foreach (TableMapping.Column column in map.Columns)
      {
        bool flag = false;
        foreach (SQLiteConnection.ColumnInfo columnInfo in tableInfo)
        {
          flag = string.Compare(column.Name, columnInfo.Name, StringComparison.OrdinalIgnoreCase) == 0;
          if (flag)
            break;
        }
        if (!flag)
          columnList.Add(column);
      }
      foreach (TableMapping.Column p in columnList)
        this.Execute("alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl(p, this.StoreDateTimeAsTicks));
    }

    /// <summary>
    /// Creates a new SQLiteCommand. Can be overridden to provide a sub-class.
    /// </summary>
    /// <seealso cref="M:SQLite.SQLiteCommand.OnInstanceCreated(System.Object)" />
    protected virtual SQLiteCommand NewCommand()
    {
      return new SQLiteCommand(this);
    }

    /// <summary>
    /// Creates a new SQLiteCommand given the command text with arguments. Place a '?'
    /// in the command text for each of the arguments.
    /// </summary>
    /// <param name="cmdText">The fully escaped SQL.</param>
    /// <param name="ps">
    /// Arguments to substitute for the occurences of '?' in the command text.
    /// </param>
    /// <returns>
    /// A <see cref="T:SQLite.SQLiteCommand" />
    /// </returns>
    public SQLiteCommand CreateCommand(string cmdText, params object[] ps)
    {
      if (!this._open)
        throw SQLiteException.New(SQLite3.Result.Error, "Cannot create commands from unopened database");
      SQLiteCommand sqLiteCommand = this.NewCommand();
      sqLiteCommand.CommandText = cmdText;
      foreach (object p in ps)
        sqLiteCommand.Bind(p);
      return sqLiteCommand;
    }

    /// <summary>
    /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
    /// in the command text for each of the arguments and then executes that command.
    /// Use this method instead of Query when you don't expect rows back. Such cases include
    /// INSERTs, UPDATEs, and DELETEs.
    /// You can set the Trace or TimeExecution properties of the connection
    /// to profile execution.
    /// </summary>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// The number of rows modified in the database as a result of this execution.
    /// </returns>
    public int Execute(string query, params object[] args)
    {
      SQLiteCommand command = this.CreateCommand(query, args);
      if (this.TimeExecution)
      {
        if (this._sw == null)
          this._sw = new Stopwatch();
        this._sw.Reset();
        this._sw.Start();
      }
      int num = command.ExecuteNonQuery();
      if (this.TimeExecution)
      {
        this._sw.Stop();
        this._elapsedMilliseconds += this._sw.ElapsedMilliseconds;
      }
      return num;
    }

    public T ExecuteScalar<T>(string query, params object[] args)
    {
      SQLiteCommand command = this.CreateCommand(query, args);
      if (this.TimeExecution)
      {
        if (this._sw == null)
          this._sw = new Stopwatch();
        this._sw.Reset();
        this._sw.Start();
      }
      T obj = command.ExecuteScalar<T>();
      if (this.TimeExecution)
      {
        this._sw.Stop();
        this._elapsedMilliseconds += this._sw.ElapsedMilliseconds;
      }
      return obj;
    }

    /// <summary>
    /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
    /// in the command text for each of the arguments and then executes that command.
    /// It returns each row of the result using the mapping automatically generated for
    /// the given type.
    /// </summary>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// An enumerable with one result for each row returned by the query.
    /// </returns>
    public List<T> Query<T>(string query, params object[] args) where T : new()
    {
      return this.CreateCommand(query, args).ExecuteQuery<T>();
    }

    /// <summary>
    /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
    /// in the command text for each of the arguments and then executes that command.
    /// It returns each row of the result using the mapping automatically generated for
    /// the given type.
    /// </summary>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// An enumerable with one result for each row returned by the query.
    /// The enumerator will call sqlite3_step on each call to MoveNext, so the database
    /// connection must remain open for the lifetime of the enumerator.
    /// </returns>
    public IEnumerable<T> DeferredQuery<T>(string query, params object[] args) where T : new()
    {
      return this.CreateCommand(query, args).ExecuteDeferredQuery<T>();
    }

    /// <summary>
    /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
    /// in the command text for each of the arguments and then executes that command.
    /// It returns each row of the result using the specified mapping. This function is
    /// only used by libraries in order to query the database via introspection. It is
    /// normally not used.
    /// </summary>
    /// <param name="map">
    /// A <see cref="T:SQLite.TableMapping" /> to use to convert the resulting rows
    /// into objects.
    /// </param>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// An enumerable with one result for each row returned by the query.
    /// </returns>
    public List<object> Query(TableMapping map, string query, params object[] args)
    {
      return this.CreateCommand(query, args).ExecuteQuery<object>(map);
    }

    /// <summary>
    /// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
    /// in the command text for each of the arguments and then executes that command.
    /// It returns each row of the result using the specified mapping. This function is
    /// only used by libraries in order to query the database via introspection. It is
    /// normally not used.
    /// </summary>
    /// <param name="map">
    /// A <see cref="T:SQLite.TableMapping" /> to use to convert the resulting rows
    /// into objects.
    /// </param>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// An enumerable with one result for each row returned by the query.
    /// The enumerator will call sqlite3_step on each call to MoveNext, so the database
    /// connection must remain open for the lifetime of the enumerator.
    /// </returns>
    public IEnumerable<object> DeferredQuery(TableMapping map, string query, params object[] args)
    {
      return this.CreateCommand(query, args).ExecuteDeferredQuery<object>(map);
    }

    /// <summary>
    /// Returns a queryable interface to the table represented by the given type.
    /// </summary>
    /// <returns>
    /// A queryable object that is able to translate Where, OrderBy, and Take
    /// queries into native SQL.
    /// </returns>
    public TableQuery<T> Table<T>() where T : new()
    {
      return new TableQuery<T>(this);
    }

    /// <summary>
    /// Attempts to retrieve an object with the given primary key from the table
    /// associated with the specified type. Use of this method requires that
    /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
    /// </summary>
    /// <param name="pk">The primary key.</param>
    /// <returns>
    /// The object with the given primary key. Throws a not found exception
    /// if the object is not found.
    /// </returns>
    public T Get<T>(object pk) where T : new()
    {
      return this.Query<T>(this.GetMapping(typeof (T), CreateFlags.None).GetByPrimaryKeySql, pk).First<T>();
    }

    /// <summary>
    /// Attempts to retrieve an object with the given primary key from the table
    /// associated with the specified type. Use of this method requires that
    /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
    /// </summary>
    /// <param name="pk">The primary key.</param>
    /// <param name="map">The TableMapping used to identify the table.</param>
    /// <returns>
    /// The object with the given primary key. Throws a not found exception
    /// if the object is not found.
    /// </returns>
    public object Get(object pk, TableMapping map)
    {
      return this.Query(map, map.GetByPrimaryKeySql, pk).First<object>();
    }

    /// <summary>
    /// Attempts to retrieve the first object that matches the predicate from the table
    /// associated with the specified type.
    /// </summary>
    /// <param name="predicate">A predicate for which object to find.</param>
    /// <returns>
    /// The object that matches the given predicate. Throws a not found exception
    /// if the object is not found.
    /// </returns>
    public T Get<T>(Expression<Func<T, bool>> predicate) where T : new()
    {
      return this.Table<T>().Where(predicate).First();
    }

    /// <summary>
    /// Attempts to retrieve an object with the given primary key from the table
    /// associated with the specified type. Use of this method requires that
    /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
    /// </summary>
    /// <param name="pk">The primary key.</param>
    /// <returns>
    /// The object with the given primary key or null
    /// if the object is not found.
    /// </returns>
    public T Find<T>(object pk) where T : new()
    {
      return this.Query<T>(this.GetMapping(typeof (T), CreateFlags.None).GetByPrimaryKeySql, pk).FirstOrDefault<T>();
    }

    /// <summary>
    /// Attempts to retrieve an object with the given primary key from the table
    /// associated with the specified type. Use of this method requires that
    /// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
    /// </summary>
    /// <param name="pk">The primary key.</param>
    /// <param name="map">The TableMapping used to identify the table.</param>
    /// <returns>
    /// The object with the given primary key or null
    /// if the object is not found.
    /// </returns>
    public object Find(object pk, TableMapping map)
    {
      return this.Query(map, map.GetByPrimaryKeySql, pk).FirstOrDefault<object>();
    }

    /// <summary>
    /// Attempts to retrieve the first object that matches the predicate from the table
    /// associated with the specified type.
    /// </summary>
    /// <param name="predicate">A predicate for which object to find.</param>
    /// <returns>
    /// The object that matches the given predicate or null
    /// if the object is not found.
    /// </returns>
    public T Find<T>(Expression<Func<T, bool>> predicate) where T : new()
    {
      return this.Table<T>().Where(predicate).FirstOrDefault();
    }

    /// <summary>
    /// Attempts to retrieve the first object that matches the query from the table
    /// associated with the specified type.
    /// </summary>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// The object that matches the given predicate or null
    /// if the object is not found.
    /// </returns>
    public T FindWithQuery<T>(string query, params object[] args) where T : new()
    {
      return this.Query<T>(query, args).FirstOrDefault<T>();
    }

    /// <summary>
    /// Attempts to retrieve the first object that matches the query from the table
    /// associated with the specified type.
    /// </summary>
    /// <param name="map">The TableMapping used to identify the table.</param>
    /// <param name="query">The fully escaped SQL.</param>
    /// <param name="args">
    /// Arguments to substitute for the occurences of '?' in the query.
    /// </param>
    /// <returns>
    /// The object that matches the given predicate or null
    /// if the object is not found.
    /// </returns>
    public object FindWithQuery(TableMapping map, string query, params object[] args)
    {
      return this.Query(map, query, args).FirstOrDefault<object>();
    }

    /// <summary>
    /// Whether <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> has been called and the database is waiting for a <see cref="M:SQLite.SQLiteConnection.Commit" />.
    /// </summary>
    public bool IsInTransaction
    {
      get
      {
        return this._transactionDepth > 0;
      }
    }

    /// <summary>
    /// Begins a new transaction. Call <see cref="M:SQLite.SQLiteConnection.Commit" /> to end the transaction.
    /// </summary>
    /// <example cref="T:System.InvalidOperationException">Throws if a transaction has already begun.</example>
    public void BeginTransaction()
    {
      if (Interlocked.CompareExchange(ref this._transactionDepth, 1, 0) != 0)
        throw new InvalidOperationException("Cannot begin a transaction while already in a transaction.");
      try
      {
        this.Execute("begin transaction");
      }
      catch (Exception ex)
      {
        SQLiteException sqLiteException = ex as SQLiteException;
        if (sqLiteException != null)
        {
          switch (sqLiteException.Result)
          {
            case SQLite3.Result.Busy:
            case SQLite3.Result.NoMem:
            case SQLite3.Result.Interrupt:
            case SQLite3.Result.IOError:
            case SQLite3.Result.Full:
              this.RollbackTo((string) null, true);
              break;
          }
        }
        else
          Interlocked.Decrement(ref this._transactionDepth);
        throw;
      }
    }

    /// <summary>
    /// Creates a savepoint in the database at the current point in the transaction timeline.
    /// Begins a new transaction if one is not in progress.
    /// 
    /// Call <see cref="M:SQLite.SQLiteConnection.RollbackTo(System.String)" /> to undo transactions since the returned savepoint.
    /// Call <see cref="M:SQLite.SQLiteConnection.Release(System.String)" /> to commit transactions after the savepoint returned here.
    /// Call <see cref="M:SQLite.SQLiteConnection.Commit" /> to end the transaction, committing all changes.
    /// </summary>
    /// <returns>A string naming the savepoint.</returns>
    public string SaveTransactionPoint()
    {
      string str = "S" + (object) this._rand.Next((int) short.MaxValue) + "D" + (object) (Interlocked.Increment(ref this._transactionDepth) - 1);
      try
      {
        this.Execute("savepoint " + str);
      }
      catch (Exception ex)
      {
        SQLiteException sqLiteException = ex as SQLiteException;
        if (sqLiteException != null)
        {
          switch (sqLiteException.Result)
          {
            case SQLite3.Result.Busy:
            case SQLite3.Result.NoMem:
            case SQLite3.Result.Interrupt:
            case SQLite3.Result.IOError:
            case SQLite3.Result.Full:
              this.RollbackTo((string) null, true);
              break;
          }
        }
        else
          Interlocked.Decrement(ref this._transactionDepth);
        throw;
      }
      return str;
    }

    /// <summary>
    /// Rolls back the transaction that was begun by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> or <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />.
    /// </summary>
    public void Rollback()
    {
      this.RollbackTo((string) null, false);
    }

    /// <summary>
    /// Rolls back the savepoint created by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> or SaveTransactionPoint.
    /// </summary>
    /// <param name="savepoint">The name of the savepoint to roll back to, as returned by <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />.  If savepoint is null or empty, this method is equivalent to a call to <see cref="M:SQLite.SQLiteConnection.Rollback" /></param>
    public void RollbackTo(string savepoint)
    {
      this.RollbackTo(savepoint, false);
    }

    /// <summary>
    /// Rolls back the transaction that was begun by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" />.
    /// </summary>
    /// <param name="savepoint">The name of the savepoint to roll back to, as returned by <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />.  If savepoint is null or empty, this method is equivalent to a call to <see cref="M:SQLite.SQLiteConnection.Rollback" /></param>
    /// <param name="noThrow">true to avoid throwing exceptions, false otherwise</param>
    private void RollbackTo(string savepoint, bool noThrow)
    {
      try
      {
        if (string.IsNullOrEmpty(savepoint))
        {
          if (Interlocked.Exchange(ref this._transactionDepth, 0) <= 0)
            return;
          this.Execute("rollback");
        }
        else
          this.DoSavePointExecute(savepoint, "rollback to ");
      }
      catch (SQLiteException ex)
      {
        if (noThrow)
          return;
        throw;
      }
    }

    /// <summary>
    /// Releases a savepoint returned from <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />.  Releasing a savepoint
    ///    makes changes since that savepoint permanent if the savepoint began the transaction,
    ///    or otherwise the changes are permanent pending a call to <see cref="M:SQLite.SQLiteConnection.Commit" />.
    /// 
    /// The RELEASE command is like a COMMIT for a SAVEPOINT.
    /// </summary>
    /// <param name="savepoint">The name of the savepoint to release.  The string should be the result of a call to <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" /></param>
    public void Release(string savepoint)
    {
      this.DoSavePointExecute(savepoint, "release ");
    }

    private void DoSavePointExecute(string savepoint, string cmd)
    {
      int num = savepoint.IndexOf('D');
      int result;
      if (num < 2 || savepoint.Length <= num + 1 || (!int.TryParse(savepoint.Substring(num + 1), out result) || 0 > result) || result >= this._transactionDepth)
        throw new ArgumentException("savePoint is not valid, and should be the result of a call to SaveTransactionPoint.", "savePoint");
      Volatile.Write(ref this._transactionDepth, result);
      this.Execute(cmd + savepoint);
    }

    /// <summary>
    /// Commits the transaction that was begun by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" />.
    /// </summary>
    public void Commit()
    {
      if (Interlocked.Exchange(ref this._transactionDepth, 0) == 0)
        return;
      this.Execute("commit");
    }

    /// <summary>
    /// Executes <paramref name="action" /> within a (possibly nested) transaction by wrapping it in a SAVEPOINT. If an
    /// exception occurs the whole transaction is rolled back, not just the current savepoint. The exception
    /// is rethrown.
    /// </summary>
    /// <param name="action">
    /// The <see cref="T:System.Action" /> to perform within a transaction. <paramref name="action" /> can contain any number
    /// of operations on the connection but should never call <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> or
    /// <see cref="M:SQLite.SQLiteConnection.Commit" />.
    /// </param>
    public void RunInTransaction(Action action)
    {
      try
      {
        string savepoint = this.SaveTransactionPoint();
        action();
        this.Release(savepoint);
      }
      catch (Exception ex)
      {
        this.Rollback();
        throw;
      }
    }

    /// <summary>Inserts all specified objects.</summary>
    /// <param name="objects">
    /// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
    /// <param name="runInTransaction" />
    /// A boolean indicating if the inserts should be wrapped in a transaction.
    /// </param>
    /// <returns>The number of rows added to the table.</returns>
    public int InsertAll(IEnumerable objects, bool runInTransaction = true)
    {
      int c = 0;
      if (runInTransaction)
      {
        this.RunInTransaction((Action) (() =>
        {
          foreach (object obj in objects)
            c += this.Insert(obj);
        }));
      }
      else
      {
        foreach (object obj in objects)
          c += this.Insert(obj);
      }
      return c;
    }

    /// <summary>Inserts all specified objects.</summary>
    /// <param name="objects">
    /// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
    /// </param>
    /// <param name="extra">
    /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
    /// </param>
    /// <param name="runInTransaction">
    /// A boolean indicating if the inserts should be wrapped in a transaction.
    /// </param>
    /// <returns>The number of rows added to the table.</returns>
    public int InsertAll(IEnumerable objects, string extra, bool runInTransaction = true)
    {
      int c = 0;
      if (runInTransaction)
      {
        this.RunInTransaction((Action) (() =>
        {
          foreach (object obj in objects)
            c += this.Insert(obj, extra);
        }));
      }
      else
      {
        foreach (object obj in objects)
          c += this.Insert(obj);
      }
      return c;
    }

    /// <summary>Inserts all specified objects.</summary>
    /// <param name="objects">
    /// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
    /// </param>
    /// <param name="objType">The type of object to insert.</param>
    /// <param name="runInTransaction">
    /// A boolean indicating if the inserts should be wrapped in a transaction.
    /// </param>
    /// <returns>The number of rows added to the table.</returns>
    public int InsertAll(IEnumerable objects, Type objType, bool runInTransaction = true)
    {
      int c = 0;
      if (runInTransaction)
      {
        this.RunInTransaction((Action) (() =>
        {
          foreach (object obj in objects)
            c += this.Insert(obj, objType);
        }));
      }
      else
      {
        foreach (object obj in objects)
          c += this.Insert(obj, objType);
      }
      return c;
    }

    /// <summary>
    /// Inserts the given object and retrieves its
    /// auto incremented primary key if it has one.
    /// </summary>
    /// <param name="obj">The object to insert.</param>
    /// <returns>The number of rows added to the table.</returns>
    public int Insert(object obj)
    {
      if (obj == null)
        return 0;
      return this.Insert(obj, "", Orm.GetType(obj));
    }

    /// <summary>
    /// Inserts the given object and retrieves its
    /// auto incremented primary key if it has one.
    /// If a UNIQUE constraint violation occurs with
    /// some pre-existing object, this function deletes
    /// the old object.
    /// </summary>
    /// <param name="obj">The object to insert.</param>
    /// <returns>The number of rows modified.</returns>
    public int InsertOrReplace(object obj)
    {
      if (obj == null)
        return 0;
      return this.Insert(obj, "OR REPLACE", Orm.GetType(obj));
    }

    /// <summary>
    /// Inserts the given object and retrieves its
    /// auto incremented primary key if it has one.
    /// </summary>
    /// <param name="obj">The object to insert.</param>
    /// <param name="objType">The type of object to insert.</param>
    /// <returns>The number of rows added to the table.</returns>
    public int Insert(object obj, Type objType)
    {
      return this.Insert(obj, "", objType);
    }

    /// <summary>
    /// Inserts the given object and retrieves its
    /// auto incremented primary key if it has one.
    /// If a UNIQUE constraint violation occurs with
    /// some pre-existing object, this function deletes
    /// the old object.
    /// </summary>
    /// <param name="obj">The object to insert.</param>
    /// <param name="objType">The type of object to insert.</param>
    /// <returns>The number of rows modified.</returns>
    public int InsertOrReplace(object obj, Type objType)
    {
      return this.Insert(obj, "OR REPLACE", objType);
    }

    /// <summary>
    /// Inserts the given object and retrieves its
    /// auto incremented primary key if it has one.
    /// </summary>
    /// <param name="obj">The object to insert.</param>
    /// <param name="extra">
    /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
    /// </param>
    /// <returns>The number of rows added to the table.</returns>
    public int Insert(object obj, string extra)
    {
      if (obj == null)
        return 0;
      return this.Insert(obj, extra, Orm.GetType(obj));
    }

    /// <summary>
    /// Inserts the given object and retrieves its
    /// auto incremented primary key if it has one.
    /// </summary>
    /// <param name="obj">The object to insert.</param>
    /// <param name="extra">
    /// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
    /// </param>
    /// <param name="objType">The type of object to insert.</param>
    /// <returns>The number of rows added to the table.</returns>
    public int Insert(object obj, string extra, Type objType)
    {
      if (obj == null || (object) objType == null)
        return 0;
      TableMapping mapping = this.GetMapping(objType, CreateFlags.None);
      if (mapping.PK != null && mapping.PK.IsAutoGuid && mapping.PK.GetValue(obj).Equals((object) Guid.Empty))
        mapping.PK.SetValue(obj, (object) Guid.NewGuid());
      TableMapping.Column[] columnArray = string.Compare(extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0 ? mapping.InsertOrReplaceColumns : mapping.InsertColumns;
      object[] source = new object[columnArray.Length];
      for (int index = 0; index < source.Length; ++index)
        source[index] = columnArray[index].GetValue(obj);
      PreparedSqlLiteInsertCommand insertCommand = mapping.GetInsertCommand(this, extra);
      int num;
      lock (insertCommand)
      {
        try
        {
          num = insertCommand.ExecuteNonQuery(source);
        }
        catch (SQLiteException ex)
        {
          if (SQLite3.ExtendedErrCode(this.Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
            throw NotNullConstraintViolationException.New(ex.Result, ex.Message, mapping, obj);
          throw;
        }
        if (mapping.HasAutoIncPK)
        {
          long id = SQLite3.LastInsertRowid(this.Handle);
          mapping.SetAutoIncPK(obj, id);
        }
      }
      if (num > 0)
        this.OnTableChanged(mapping, NotifyTableChangedAction.Insert);
      return num;
    }

    /// <summary>
    /// Updates all of the columns of a table using the specified object
    /// except for its primary key.
    /// The object is required to have a primary key.
    /// </summary>
    /// <param name="obj">
    /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
    /// </param>
    /// <returns>The number of rows updated.</returns>
    public int Update(object obj)
    {
      if (obj == null)
        return 0;
      return this.Update(obj, Orm.GetType(obj));
    }

    /// <summary>
    /// Updates all of the columns of a table using the specified object
    /// except for its primary key.
    /// The object is required to have a primary key.
    /// </summary>
    /// <param name="obj">
    /// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
    /// </param>
    /// <param name="objType">The type of object to insert.</param>
    /// <returns>The number of rows updated.</returns>
    public int Update(object obj, Type objType)
    {
      if (obj == null || (object) objType == null)
        return 0;
      TableMapping mapping = this.GetMapping(objType, CreateFlags.None);
      TableMapping.Column pk = mapping.PK;
      if (pk == null)
        throw new NotSupportedException("Cannot update " + mapping.TableName + ": it has no PK");
      IEnumerable<TableMapping.Column> source = ((IEnumerable<TableMapping.Column>) mapping.Columns).Where<TableMapping.Column>((Func<TableMapping.Column, bool>) (p => p != pk));
      List<object> objectList = new List<object>(source.Select<TableMapping.Column, object>((Func<TableMapping.Column, object>) (c => c.GetValue(obj))));
      objectList.Add(pk.GetValue(obj));
      string query = string.Format("update \"{0}\" set {1} where {2} = ? ", new object[3]
      {
        (object) mapping.TableName,
        (object) string.Join(",", source.Select<TableMapping.Column, string>((Func<TableMapping.Column, string>) (c => "\"" + c.Name + "\" = ? ")).ToArray<string>()),
        (object) pk.Name
      });
      int num;
      try
      {
        num = this.Execute(query, objectList.ToArray());
      }
      catch (SQLiteException ex)
      {
        if (ex.Result == SQLite3.Result.Constraint && SQLite3.ExtendedErrCode(this.Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
          throw NotNullConstraintViolationException.New(ex, mapping, obj);
        throw ex;
      }
      if (num > 0)
        this.OnTableChanged(mapping, NotifyTableChangedAction.Update);
      return num;
    }

    /// <summary>Updates all specified objects.</summary>
    /// <param name="objects">
    /// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
    /// </param>
    /// <param name="runInTransaction">
    /// A boolean indicating if the inserts should be wrapped in a transaction
    /// </param>
    /// <returns>The number of rows modified.</returns>
    public int UpdateAll(IEnumerable objects, bool runInTransaction = true)
    {
      int c = 0;
      if (runInTransaction)
      {
        this.RunInTransaction((Action) (() =>
        {
          foreach (object obj in objects)
            c += this.Update(obj);
        }));
      }
      else
      {
        foreach (object obj in objects)
          c += this.Update(obj);
      }
      return c;
    }

    /// <summary>
    /// Deletes the given object from the database using its primary key.
    /// </summary>
    /// <param name="objectToDelete">
    /// The object to delete. It must have a primary key designated using the PrimaryKeyAttribute.
    /// </param>
    /// <returns>The number of rows deleted.</returns>
    public int Delete(object objectToDelete)
    {
      TableMapping mapping = this.GetMapping(Orm.GetType(objectToDelete), CreateFlags.None);
      TableMapping.Column pk = mapping.PK;
      if (pk == null)
        throw new NotSupportedException("Cannot delete " + mapping.TableName + ": it has no PK");
      int num = this.Execute(string.Format("delete from \"{0}\" where \"{1}\" = ?", new object[2]
      {
        (object) mapping.TableName,
        (object) pk.Name
      }), pk.GetValue(objectToDelete));
      if (num > 0)
        this.OnTableChanged(mapping, NotifyTableChangedAction.Delete);
      return num;
    }

    /// <summary>Deletes the object with the specified primary key.</summary>
    /// <param name="primaryKey">The primary key of the object to delete.</param>
    /// <returns>The number of objects deleted.</returns>
    /// <typeparam name="T">The type of object.</typeparam>
    public int Delete<T>(object primaryKey)
    {
      return this.Delete(primaryKey, this.GetMapping(typeof (T), CreateFlags.None));
    }

    /// <summary>Deletes the object with the specified primary key.</summary>
    /// <param name="primaryKey">The primary key of the object to delete.</param>
    /// <param name="map">The TableMapping used to identify the table.</param>
    /// <returns>The number of objects deleted.</returns>
    public int Delete(object primaryKey, TableMapping map)
    {
      TableMapping.Column pk = map.PK;
      if (pk == null)
        throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK");
      int num = this.Execute(string.Format("delete from \"{0}\" where \"{1}\" = ?", new object[2]
      {
        (object) map.TableName,
        (object) pk.Name
      }), primaryKey);
      if (num > 0)
        this.OnTableChanged(map, NotifyTableChangedAction.Delete);
      return num;
    }

    /// <summary>
    /// Deletes all the objects from the specified table.
    /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
    /// specified table. Do you really want to do that?
    /// </summary>
    /// <returns>The number of objects deleted.</returns>
    /// <typeparam name="T">The type of objects to delete.</typeparam>
    public int DeleteAll<T>()
    {
      return this.DeleteAll(this.GetMapping(typeof (T), CreateFlags.None));
    }

    /// <summary>
    /// Deletes all the objects from the specified table.
    /// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
    /// specified table. Do you really want to do that?
    /// </summary>
    /// <param name="map">The TableMapping used to identify the table.</param>
    /// <returns>The number of objects deleted.</returns>
    public int DeleteAll(TableMapping map)
    {
      int num = this.Execute(string.Format("delete from \"{0}\"", new object[1]
      {
        (object) map.TableName
      }));
      if (num > 0)
        this.OnTableChanged(map, NotifyTableChangedAction.Delete);
      return num;
    }

    ~SQLiteConnection()
    {
      this.Dispose(false);
    }

    public void Dispose()
    {
      this.Dispose(true);
      GC.SuppressFinalize((object) this);
    }

    public void Close()
    {
      this.Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (!this._open)
        return;
      if (this.Handle == SQLiteConnection.NullHandle)
        return;
      try
      {
        if (disposing)
        {
          if (this._mappings != null)
          {
            foreach (TableMapping tableMapping in this._mappings.Values)
              tableMapping.Dispose();
          }
          SQLite3.Result r = SQLite3.Close2(this.Handle);
          if (r != SQLite3.Result.OK)
          {
            string errmsg = SQLite3.GetErrmsg(this.Handle);
            throw SQLiteException.New(r, errmsg);
          }
        }
        else
        {
          int num = (int) SQLite3.Close2(this.Handle);
        }
      }
      finally
      {
        this.Handle = SQLiteConnection.NullHandle;
        this._open = false;
      }
    }

    private void OnTableChanged(TableMapping table, NotifyTableChangedAction action)
    {
      // ISSUE: reference to a compiler-generated field
      EventHandler<NotifyTableChangedEventArgs> tableChanged = this.TableChanged;
      if (tableChanged == null)
        return;
      tableChanged((object) this, new NotifyTableChangedEventArgs(table, action));
    }

    public event EventHandler<NotifyTableChangedEventArgs> TableChanged;

    private struct IndexedColumn
    {
      public int Order;
      public string ColumnName;
    }

    private struct IndexInfo
    {
      public string IndexName;
      public string TableName;
      public bool Unique;
      public List<SQLiteConnection.IndexedColumn> Columns;
    }

    public class ColumnInfo
    {
      [Column("name")]
      public string Name { get; set; }

      public int notnull { get; set; }

      public override string ToString()
      {
        return this.Name;
      }
    }
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值