ServiceStack.OrmLite - slevenqin/blog.github.io GitHub Wiki

Insert 语法

扩展支持 insert into tablename(field,...) values (value1,...),(value2,...),(value3,...);

经过测试 原来的InsertAll和扩展的方法、原生手写Insert Sql还要快.

10000笔记录

  1. 已实现的InsertAll 8秒多
  2. 扩展120多秒
  3. 原生手写insert sql 80多秒

IOrmLiteDialectProvider

// // ServiceStack.OrmLite: Light-weight POCO ORM for .NET and Mono // // Authors: // Demis Bellot ([email protected]) // // Copyright 2013 ServiceStack, Inc. All Rights Reserved. // // Licensed under the same terms of ServiceStack. //

using System; using System.Collections.Generic; using System.Data; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using ServiceStack.OrmLite.Converters; using ServiceStack.Text;

namespace ServiceStack.OrmLite { public interface IOrmLiteDialectProvider { void RegisterConverter(IOrmLiteConverter converter);

    /// <summary>
    /// Invoked when a DB Connection is opened
    /// </summary>
    Action<IDbConnection> OnOpenConnection { get; set; } 
        

    IOrmLiteExecFilter ExecFilter { get; set; }

    /// <summary>
    /// Gets the explicit Converter registered for a specific type
    /// </summary>
    IOrmLiteConverter GetConverter(Type type);

    /// <summary>
    /// Return best matching converter, falling back to Enum, Value or Ref Type Converters
    /// </summary>
    IOrmLiteConverter GetConverterBestMatch(Type type);
    
    IOrmLiteConverter GetConverterBestMatch(FieldDefinition fieldDef);

    string ParamString { get; set; }

    string EscapeWildcards(string value);

    INamingStrategy NamingStrategy { get; set; }

    IStringSerializer StringSerializer { get; set; }

    Func<string, string> ParamNameFilter { get; set; }
    
    Dictionary<string, string> Variables { get; }

    /// <summary>
    /// Quote the string so that it can be used inside an SQL-expression
    /// Escape quotes inside the string
    /// </summary>
    /// <param name="paramValue"></param>
    /// <returns></returns>
    string GetQuotedValue(string paramValue);

    string GetQuotedValue(object value, Type fieldType);

    string GetDefaultValue(Type tableType, string fieldName);

    string GetDefaultValue(FieldDefinition fieldDef);

    bool HasInsertReturnValues(ModelDefinition modelDef);

    object GetParamValue(object value, Type fieldType);

    object ToDbValue(object value, Type type);

    object FromDbValue(object value, Type type);

    object GetValue(IDataReader reader, int columnIndex, Type type);

    int GetValues(IDataReader reader, object[] values);

    IDbConnection CreateConnection(string filePath, Dictionary<string, string> options);

    string GetTableName(ModelDefinition modelDef);

    string GetTableName(string tableName, string schema = null);

    string GetQuotedTableName(ModelDefinition modelDef);

    string GetQuotedTableName(string tableName, string schema=null);

    string GetQuotedColumnName(string columnName);

    string GetQuotedName(string columnName);

    string SanitizeFieldNameForParamName(string fieldName);

    string GetColumnDefinition(FieldDefinition fieldDef);

    long GetLastInsertId(IDbCommand command);

    string GetLastInsertIdSqlSuffix<T>();

    string ToSelectStatement(Type tableType, string sqlFilter, params object[] filterParams);

    string ToSelectStatement(ModelDefinition modelDef, string selectExpression, string bodyExpression, string orderByExpression = null, int? offset = null, int? rows = null);

    string ToInsertRowStatement(IDbCommand cmd, object objWithProperties, ICollection<string> insertFields = null);

    void PrepareParameterizedInsertStatement<T>(IDbCommand cmd, ICollection<string> insertFields = null);

    /// <returns>If had RowVersion</returns>
    bool PrepareParameterizedUpdateStatement<T>(IDbCommand cmd, ICollection<string> updateFields = null);

    /// <returns>If had RowVersion</returns>
    bool PrepareParameterizedDeleteStatement<T>(IDbCommand cmd, IDictionary<string, object> deleteFieldValues);

    void PrepareStoredProcedureStatement<T>(IDbCommand cmd, T obj);

    void SetParameterValues<T>(IDbCommand dbCmd, object obj);

    void SetParameter(FieldDefinition fieldDef, IDbDataParameter p);

    void SetParameterValue<T>(FieldDefinition fieldDef, IDataParameter p, object obj);

    Dictionary<string, FieldDefinition> GetFieldDefinitionMap(ModelDefinition modelDef);

    object GetFieldValue(FieldDefinition fieldDef, object value);
    object GetFieldValue(Type fieldType, object value);

    void PrepareUpdateRowStatement(IDbCommand dbCmd, object objWithProperties, ICollection<string> updateFields = null);

    void PrepareUpdateRowStatement<T>(IDbCommand dbCmd, Dictionary<string, object> args, string sqlFilter);

    void PrepareUpdateRowAddStatement<T>(IDbCommand dbCmd, Dictionary<string, object> args, string sqlFilter);

    void PrepareInsertRowStatement<T>(IDbCommand dbCmd, Dictionary<string, object> args);

    string ToDeleteStatement(Type tableType, string sqlFilter, params object[] filterParams);

    IDbCommand CreateParameterizedDeleteStatement(IDbConnection connection, object objWithProperties);

    string ToExistStatement(Type fromTableType,
        object objWithProperties,
        string sqlFilter,
        params object[] filterParams);

    string ToSelectFromProcedureStatement(object fromObjWithProperties,
        Type outputModelType,
        string sqlFilter,
        params object[] filterParams);

    string ToExecuteProcedureStatement(object objWithProperties);

    string ToCreateTableStatement(Type tableType);
    string ToPostCreateTableStatement(ModelDefinition modelDef);
    string ToPostDropTableStatement(ModelDefinition modelDef);

    List<string> ToCreateIndexStatements(Type tableType);
    List<string> ToCreateSequenceStatements(Type tableType);
    string ToCreateSequenceStatement(Type tableType, string sequenceName);

    List<string> SequenceList(Type tableType);

    bool DoesTableExist(IDbConnection db, string tableName, string schema = null);
    bool DoesTableExist(IDbCommand dbCmd, string tableName, string schema = null);
    bool DoesColumnExist(IDbConnection db, string columnName, string tableName, string schema = null);
    bool DoesSequenceExist(IDbCommand dbCmd, string sequenceName);

    void DropColumn(IDbConnection db, Type modelType, string columnName);

    object FromDbRowVersion(Type fieldType,  object value);

    SelectItem GetRowVersionSelectColumn(FieldDefinition field, string tablePrefix = null);
    string GetRowVersionColumn(FieldDefinition field, string tablePrefix = null);

    string GetColumnNames(ModelDefinition modelDef);
    SelectItem[] GetColumnNames(ModelDefinition modelDef, string tablePrefix);

    SqlExpression<T> SqlExpression<T>();

    IDbDataParameter CreateParam();

    //DDL
    string GetDropForeignKeyConstraints(ModelDefinition modelDef);

    string ToAddColumnStatement(Type modelType, FieldDefinition fieldDef);
    string ToAlterColumnStatement(Type modelType, FieldDefinition fieldDef);
    string ToChangeColumnNameStatement(Type modelType, FieldDefinition fieldDef, string oldColumnName);
    string ToAddForeignKeyStatement<T, TForeign>(Expression<Func<T, object>> field,
                                                 Expression<Func<TForeign, object>> foreignField,
                                                 OnFkOption onUpdate,
                                                 OnFkOption onDelete,
                                                 string foreignKeyName = null);
    string ToCreateIndexStatement<T>(Expression<Func<T, object>> field,
                                     string indexName = null, bool unique = false);

    //Async
    Task OpenAsync(IDbConnection db, CancellationToken token = default(CancellationToken));
    Task<IDataReader> ExecuteReaderAsync(IDbCommand cmd, CancellationToken token = default(CancellationToken));
    Task<int> ExecuteNonQueryAsync(IDbCommand cmd, CancellationToken token = default(CancellationToken));
    Task<object> ExecuteScalarAsync(IDbCommand cmd, CancellationToken token = default(CancellationToken));
    Task<bool> ReadAsync(IDataReader reader, CancellationToken token = default(CancellationToken));
    Task<List<T>> ReaderEach<T>(IDataReader reader, Func<T> fn, CancellationToken token = default(CancellationToken));
    Task<Return> ReaderEach<Return>(IDataReader reader, Action fn, Return source, CancellationToken token = default(CancellationToken));
    Task<T> ReaderRead<T>(IDataReader reader, Func<T> fn, CancellationToken token = default(CancellationToken));

    Task<long> InsertAndGetLastInsertIdAsync<T>(IDbCommand dbCmd, CancellationToken token);

    string GetLoadChildrenSubSelect<From>(SqlExpression<From> expr);
    string ToRowCountStatement(string innerSql);

    string ToUpdateStatement<T>(IDbCommand dbCmd, T item, ICollection<string> updateFields = null);
    string ToInsertStatement<T>(IDbCommand dbCmd, T item, ICollection<string> insertFields = null);
    string MergeParamsIntoSql(string sql, IEnumerable<IDbDataParameter> dbParams);

    string SqlConflict(string sql, string conflictResolution);

    string SqlConcat(IEnumerable<object> args);
    string SqlCurrency(string fieldOrValue);
    string SqlCurrency(string fieldOrValue, string currencySymbol);
    string SqlBool(bool value);
    string SqlLimit(int? offset = null, int? rows = null);
    string SqlCast(object fieldOrValue, string castAs);
}

}

OrmLiteInsertTests

[Test] public void Can_insert_into_InsertMultiValues_ModelWithFieldsOfDifferentTypes_table() { var len = 10000; using (var db = OpenDbConnection()) { db.CreateTable(true); ModelWithFieldsOfDifferentTypes[] objs = new ModelWithFieldsOfDifferentTypes[len]; for (int i = 0; i < len; i++) { objs[i] = ModelWithFieldsOfDifferentTypes.Create(i); } var stopWatch = new Stopwatch(); stopWatch.Start(); // var loop = len + 1; // string execSql = @" //insert into modelwithfieldsofdifferenttypes(Id, Name, Int, Long, Guid, Bool, DateTime, TimeSpan, Double, Decimal) values "; // Dictionary<string, object> dictionaryParam = new Dictionary<string, object>(); // for (int i = 0; i < len; i++) // { // execSql += $" (@Id_{i},@Name_{i},@Int_{i},@Long_{i},@Guid_{i},@Bool_{i},@DateTime_{i},@TimeSpan_{i},@Double_{i},@Decimal_{i}) "; // if (i != objs.Length - 1) // { // execSql += ","; // } // dictionaryParam.Add($"@Id_{i}", i+1); // dictionaryParam.Add($"@Name_{i}", objs[i].Name); // dictionaryParam.Add($"@Int_{i}", objs[i].Int); // dictionaryParam.Add($"@Long_{i}", objs[i].Long); // dictionaryParam.Add($"@Guid_{i}", objs[i].Guid); // dictionaryParam.Add($"@Bool_{i}", objs[i].Bool); // dictionaryParam.Add($"@DateTime_{i}", objs[i].DateTime); // dictionaryParam.Add($"@TimeSpan_{i}", 0); // dictionaryParam.Add($"@Double_{i}", objs[i].Double); // dictionaryParam.Add($"@Decimal_{i}", objs[i].Decimal); // } // execSql += ";";

// stopWatch.Stop();

// var mm = stopWatch.ElapsedMilliseconds;

// stopWatch.Start();

// db.ExecuteNonQuery(execSql, dictionaryParam);

            //db.InsertMultiValues(objs);
            db.InsertAll(objs);
            stopWatch.Stop();
            var millionSecond = stopWatch.ElapsedMilliseconds;
        }
    }

OrmLiteWriteApi

using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using ServiceStack.Data; using ServiceStack.Text;

namespace ServiceStack.OrmLite { public static class OrmLiteWriteApi { ///

/// Get the last SQL statement that was executed. /// public static string GetLastSql(this IDbConnection dbConn) { return dbConn is OrmLiteConnection ormLiteConn ? ormLiteConn.LastCommandText : null; }
    public static string GetLastSqlAndParams(this IDbCommand dbCmd)
    {
        var sb = StringBuilderCache.Allocate();
        sb.AppendLine(dbCmd.CommandText)
          .AppendLine("PARAMS: ");

        foreach (IDataParameter parameter in dbCmd.Parameters)
        {
            sb.Append(parameter.ParameterName).Append(": ")
                .Append(parameter.Value.ToJsv())
                .Append(" : ").AppendLine((parameter.Value ?? DBNull.Value).GetType().Name);
        }
        sb.AppendLine();

        return StringBuilderCache.ReturnAndFree(sb);
    }

    /// <summary>
    /// Execute any arbitrary raw SQL.
    /// </summary>
    /// <returns>number of rows affected</returns>
    public static int ExecuteSql(this IDbConnection dbConn, string sql)
    {
        return dbConn.Exec(dbCmd => dbCmd.ExecuteSql(sql));
    }

    /// <summary>
    /// Execute any arbitrary raw SQL with db params.
    /// </summary>
    /// <returns>number of rows affected</returns>
    public static int ExecuteSql(this IDbConnection dbConn, string sql, object dbParams)
    {
        return dbConn.Exec(dbCmd => dbCmd.ExecuteSql(sql, dbParams));
    }

    /// <summary>
    /// Execute any arbitrary raw SQL with db params.
    /// </summary>
    /// <returns>number of rows affected</returns>
    public static int ExecuteSql(this IDbConnection dbConn, string sql, Dictionary<string,object> dbParams)
    {
        return dbConn.Exec(dbCmd => dbCmd.ExecuteSql(sql, dbParams));
    }

    /// <summary>
    /// Insert 1 POCO, use selectIdentity to retrieve the last insert AutoIncrement id (if any). E.g:
    /// <para>var id = db.Insert(new Person { Id = 1, FirstName = "Jimi }, selectIdentity:true)</para>
    /// </summary>
    public static long Insert<T>(this IDbConnection dbConn, T obj, bool selectIdentity = false)
    {
        return dbConn.Exec(dbCmd => dbCmd.Insert(obj, commandFilter: null, selectIdentity: selectIdentity));
    }

    /// <summary>
    /// Insert 1 POCO and modify populated IDbCommand with a commandFilter. E.g:
    /// <para>var id = db.Insert(new Person { Id = 1, FirstName = "Jimi }, dbCmd => applyFilter(dbCmd))</para>
    /// </summary>
    public static long Insert<T>(this IDbConnection dbConn, T obj, Action<IDbCommand> commandFilter, bool selectIdentity = false)
    {
        return dbConn.Exec(dbCmd => dbCmd.Insert(obj, commandFilter: commandFilter, selectIdentity: selectIdentity));
    }

    /// <summary>
    /// Insert 1 or more POCOs in a transaction using Table default values when defined. E.g:
    /// <para>db.InsertUsingDefaults(new Person { FirstName = "Tupac", LastName = "Shakur" },</para>
    /// <para>                       new Person { FirstName = "Biggie", LastName = "Smalls" })</para>
    /// </summary>
    public static void InsertUsingDefaults<T>(this IDbConnection dbConn, params T[] objs)
    {
        dbConn.Exec(dbCmd => dbCmd.InsertUsingDefaults(objs));
    }

    /// <summary>
    /// Insert results from SELECT SqlExpression, use selectIdentity to retrieve the last insert AutoIncrement id (if any). E.g:
    /// <para>db.InsertIntoSelect&lt;Contact&gt;(db.From&lt;Person&gt;().Select(x => new { x.Id, Surname == x.LastName }))</para>
    /// </summary>
    public static long InsertIntoSelect<T>(this IDbConnection dbConn, ISqlExpression query)
    {
        return dbConn.Exec(dbCmd => dbCmd.InsertIntoSelect<T>(query, commandFilter: null));
    }

    /// <summary>
    /// Insert results from SELECT SqlExpression, use selectIdentity to retrieve the last insert AutoIncrement id (if any). E.g:
    /// <para>db.InsertIntoSelect&lt;Contact&gt;(db.From&lt;Person&gt;().Select(x => new { x.Id, Surname == x.LastName }))</para>
    /// </summary>
    public static long InsertIntoSelect<T>(this IDbConnection dbConn, ISqlExpression query, Action<IDbCommand> commandFilter)
    {
        return dbConn.Exec(dbCmd => dbCmd.InsertIntoSelect<T>(query, commandFilter: commandFilter));
    }

    /// <summary>
    /// Insert a collection of POCOs in a transaction. E.g:
    /// <para>db.InsertAll(new[] { new Person { Id = 9, FirstName = "Biggie", LastName = "Smalls", Age = 24 } })</para>
    /// </summary>
    public static void InsertAll<T>(this IDbConnection dbConn, IEnumerable<T> objs)
    {
        dbConn.Exec(dbCmd => dbCmd.InsertAll(objs, commandFilter:null));
    }

    /// <summary>
    /// Insert a collection of POCOs in a transaction and modify populated IDbCommand with a commandFilter. E.g:
    /// <para>db.InsertAll(new[] { new Person { Id = 9, FirstName = "Biggie", LastName = "Smalls", Age = 24 } },</para>
    /// <para>             dbCmd => applyFilter(dbCmd))</para>
    /// </summary>
    public static void InsertAll<T>(this IDbConnection dbConn, IEnumerable<T> objs, Action<IDbCommand> commandFilter)
    {
        dbConn.Exec(dbCmd => dbCmd.InsertAll(objs, commandFilter: commandFilter));
    }

    /// <summary>
    /// Insert 1 or more POCOs in a transaction. E.g:
    /// <para>db.Insert(new Person { Id = 1, FirstName = "Tupac", LastName = "Shakur", Age = 25 },</para>
    /// <para>          new Person { Id = 2, FirstName = "Biggie", LastName = "Smalls", Age = 24 })</para>
    /// </summary>
    public static void Insert<T>(this IDbConnection dbConn, params T[] objs)
    {
        dbConn.Exec(dbCmd => dbCmd.Insert(commandFilter: null, objs: objs));
    }

    /// <summary>
    /// Insert 1 or more POCOs in a transaction. E.g:
    /// <para>db.Insert(new Person { Id = 1, FirstName = "Tupac", LastName = "Shakur", Age = 25 },</para>
    /// <para>          new Person { Id = 2, FirstName = "Biggie", LastName = "Smalls", Age = 24 })</para>
    /// INSERT INTO tbl_name(a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
    /// </summary>
    public static void InsertMultiValues<T>(this IDbConnection dbConn, params T[] objs)
    {
        dbConn.Exec(dbCmd => dbCmd.InsertMultiValues(commandFilter: null, objs: objs));
    }

    /// <summary>
    /// Insert 1 or more POCOs in a transaction and modify populated IDbCommand with a commandFilter. E.g:
    /// <para>db.Insert(dbCmd => applyFilter(dbCmd),</para>
    /// <para>          new Person { Id = 1, FirstName = "Tupac", LastName = "Shakur", Age = 25 },</para>
    /// <para>          new Person { Id = 2, FirstName = "Biggie", LastName = "Smalls", Age = 24 })</para>
    /// </summary>
    public static void Insert<T>(this IDbConnection dbConn, Action<IDbCommand> commandFilter, params T[] objs)
    {
        dbConn.Exec(dbCmd => dbCmd.Insert(commandFilter: commandFilter, objs: objs));
    }

    /// <summary>
    /// Updates 1 POCO. All fields are updated except for the PrimaryKey which is used as the identity selector. E.g:
    /// <para>db.Update(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 })</para>
    /// </summary>
    public static int Update<T>(this IDbConnection dbConn, T obj, Action<IDbCommand> commandFilter = null)
    {
        return dbConn.Exec(dbCmd => dbCmd.Update(obj, commandFilter));
    }

    /// <summary>
    /// Updates 1 or more POCOs in a transaction. E.g:
    /// <para>db.Update(new Person { Id = 1, FirstName = "Tupac", LastName = "Shakur", Age = 25 },</para>
    /// <para>new Person { Id = 2, FirstName = "Biggie", LastName = "Smalls", Age = 24 })</para>
    /// </summary>
    public static int Update<T>(this IDbConnection dbConn, params T[] objs)
    {
        return dbConn.Exec(dbCmd => dbCmd.Update(objs, commandFilter:null));
    }
    public static int Update<T>(this IDbConnection dbConn, Action<IDbCommand> commandFilter, params T[] objs)
    {
        return dbConn.Exec(dbCmd => dbCmd.Update(objs, commandFilter));
    }

    /// <summary>
    /// Updates 1 or more POCOs in a transaction. E.g:
    /// <para>db.UpdateAll(new[] { new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 } })</para>
    /// </summary>
    public static int UpdateAll<T>(this IDbConnection dbConn, IEnumerable<T> objs, Action<IDbCommand> commandFilter = null)
    {
        return dbConn.Exec(dbCmd => dbCmd.UpdateAll(objs, commandFilter));
    }

    /// <summary>
    /// Delete rows using an anonymous type filter. E.g:
    /// <para>db.Delete&lt;Person&gt;(new { FirstName = "Jimi", Age = 27 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int Delete<T>(this IDbConnection dbConn, object anonFilter)
    {
        return dbConn.Exec(dbCmd => dbCmd.Delete<T>(anonFilter));
    }

    /// <summary>
    /// Delete 1 row using all fields in the filter. E.g:
    /// <para>db.Delete(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int Delete<T>(this IDbConnection dbConn, T allFieldsFilter)
    {
        return dbConn.Exec(dbCmd => dbCmd.Delete(allFieldsFilter));
    }

    /// <summary>
    /// Delete 1 or more rows in a transaction using all fields in the filter. E.g:
    /// <para>db.Delete(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix", Age = 27 })</para>
    /// </summary>
    public static int Delete<T>(this IDbConnection dbConn, params T[] allFieldsFilters)
    {
        return dbConn.Exec(dbCmd => dbCmd.Delete(allFieldsFilters));
    }

    /// <summary>
    /// Delete 1 or more rows using only field with non-default values in the filter. E.g:
    /// <para>db.DeleteNonDefaults(new Person { FirstName = "Jimi", Age = 27 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteNonDefaults<T>(this IDbConnection dbConn, T nonDefaultsFilter)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteNonDefaults(nonDefaultsFilter));
    }

    /// <summary>
    /// Delete 1 or more rows in a transaction using only field with non-default values in the filter. E.g:
    /// <para>db.DeleteNonDefaults(new Person { FirstName = "Jimi", Age = 27 }, 
    /// new Person { FirstName = "Janis", Age = 27 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteNonDefaults<T>(this IDbConnection dbConn, params T[] nonDefaultsFilters)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteNonDefaults(nonDefaultsFilters));
    }

    /// <summary>
    /// Delete 1 row by the PrimaryKey. E.g:
    /// <para>db.DeleteById&lt;Person&gt;(1)</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteById<T>(this IDbConnection dbConn, object id)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteById<T>(id));
    }

    /// <summary>
    /// Delete 1 row by the PrimaryKey where the rowVersion matches the optimistic concurrency field. 
    /// Will throw <exception cref="OptimisticConcurrencyException">RowModefiedExeption</exception> if the 
    /// row does not exist or has a different row version.
    /// E.g: <para>db.DeleteById&lt;Person&gt;(1)</para>
    /// </summary>
    public static void DeleteById<T>(this IDbConnection dbConn, object id, ulong rowVersion)
    {
        dbConn.Exec(dbCmd => dbCmd.DeleteById<T>(id, rowVersion));
    }

    /// <summary>
    /// Delete all rows identified by the PrimaryKeys. E.g:
    /// <para>db.DeleteById&lt;Person&gt;(new[] { 1, 2, 3 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteByIds<T>(this IDbConnection dbConn, IEnumerable idValues)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteByIds<T>(idValues));
    }

    /// <summary>
    /// Delete all rows in the generic table type. E.g:
    /// <para>db.DeleteAll&lt;Person&gt;()</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteAll<T>(this IDbConnection dbConn)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteAll<T>());
    }

    /// <summary>
    /// Delete all rows provided. E.g:
    /// <para>db.DeleteAll&lt;Person&gt;(people)</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteAll<T>(this IDbConnection dbConn, IEnumerable<T> rows)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteAll(rows));
    }

    /// <summary>
    /// Delete all rows in the runtime table type. E.g:
    /// <para>db.DeleteAll(typeof(Person))</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int DeleteAll(this IDbConnection dbConn, Type tableType)
    {
        return dbConn.Exec(dbCmd => dbCmd.DeleteAll(tableType));
    }

    /// <summary>
    /// Delete rows using a SqlFormat filter. E.g:
    /// <para>db.Delete&lt;Person&gt;("Age > @age", new { age = 42 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int Delete<T>(this IDbConnection dbConn, string sqlFilter, object anonType)
    {
        return dbConn.Exec(dbCmd => dbCmd.Delete<T>(sqlFilter, anonType));
    }

    /// <summary>
    /// Delete rows using a SqlFormat filter. E.g:
    /// <para>db.Delete(typeof(Person), "Age > @age", new { age = 42 })</para>
    /// </summary>
    /// <returns>number of rows deleted</returns>
    public static int Delete(this IDbConnection dbConn, Type tableType, string sqlFilter, object anonType)
    {
        return dbConn.Exec(dbCmd => dbCmd.Delete(tableType, sqlFilter, anonType));
    }

    /// <summary>
    /// Insert a new row or update existing row. Returns true if a new row was inserted. 
    /// Optional references param decides whether to save all related references as well. E.g:
    /// <para>db.Save(customer, references:true)</para>
    /// </summary>
    /// <returns>true if a row was inserted; false if it was updated</returns>
    public static bool Save<T>(this IDbConnection dbConn, T obj, bool references = false)
    {
        if (!references)
            return dbConn.Exec(dbCmd => dbCmd.Save(obj));

        return dbConn.Exec(dbCmd =>
        {
            var ret = dbCmd.Save(obj);
            dbCmd.SaveAllReferences(obj);
            return ret;
        });
    }

    /// <summary>
    /// Insert new rows or update existing rows. Return number of rows added E.g:
    /// <para>db.Save(new Person { Id = 10, FirstName = "Amy", LastName = "Winehouse", Age = 27 })</para>
    /// </summary>
    /// <returns>number of rows added</returns>
    public static int Save<T>(this IDbConnection dbConn, params T[] objs)
    {
        return dbConn.Exec(dbCmd => dbCmd.Save(objs));
    }

    /// <summary>
    /// Insert new rows or update existing rows. Return number of rows added E.g:
    /// <para>db.SaveAll(new [] { new Person { Id = 10, FirstName = "Amy", LastName = "Winehouse", Age = 27 } })</para>
    /// </summary>
    /// <returns>number of rows added</returns>
    public static int SaveAll<T>(this IDbConnection dbConn, IEnumerable<T> objs)
    {
        return dbConn.Exec(dbCmd => dbCmd.SaveAll(objs));
    }

    /// <summary>
    /// Populates all related references on the instance with its primary key and saves them. Uses '(T)Id' naming convention. E.g:
    /// <para>db.SaveAllReferences(customer)</para> 
    /// </summary>
    public static void SaveAllReferences<T>(this IDbConnection dbConn, T instance)
    {
        dbConn.Exec(dbCmd => dbCmd.SaveAllReferences(instance));
    }

    /// <summary>
    /// Populates the related references with the instance primary key and saves them. Uses '(T)Id' naming convention. E.g:
    /// <para>db.SaveReference(customer, customer.Orders)</para> 
    /// </summary>
    public static void SaveReferences<T, TRef>(this IDbConnection dbConn, T instance, params TRef[] refs)
    {
        dbConn.Exec(dbCmd => dbCmd.SaveReferences(instance, refs));
    }

    /// <summary>
    /// Populates the related references with the instance primary key and saves them. Uses '(T)Id' naming convention. E.g:
    /// <para>db.SaveReference(customer, customer.Orders)</para> 
    /// </summary>
    public static void SaveReferences<T, TRef>(this IDbConnection dbConn, T instance, List<TRef> refs)
    {
        dbConn.Exec(dbCmd => dbCmd.SaveReferences(instance, refs.ToArray()));
    }

    /// <summary>
    /// Populates the related references with the instance primary key and saves them. Uses '(T)Id' naming convention. E.g:
    /// <para>db.SaveReferences(customer, customer.Orders)</para> 
    /// </summary>
    public static void SaveReferences<T, TRef>(this IDbConnection dbConn, T instance, IEnumerable<TRef> refs)
    {
        dbConn.Exec(dbCmd => dbCmd.SaveReferences(instance, refs.ToArray()));
    }

    // Procedures
    public static void ExecuteProcedure<T>(this IDbConnection dbConn, T obj)
    {
        dbConn.Exec(dbCmd => dbCmd.ExecuteProcedure(obj));
    }

    /// <summary>
    /// Generates inline UPDATE SQL Statement
    /// </summary>
    public static string ToUpdateStatement<T>(this IDbConnection dbConn, T item, ICollection<string> updateFields = null)
    {
        return dbConn.Exec(dbCmd => dbCmd.GetDialectProvider().ToUpdateStatement(dbCmd, item, updateFields));
    }

    /// <summary>
    /// Generates inline INSERT SQL Statement
    /// </summary>
    public static string ToInsertStatement<T>(this IDbConnection dbConn, T item, ICollection<string> insertFields = null)
    {
        return dbConn.Exec(dbCmd => dbCmd.GetDialectProvider().ToInsertStatement(dbCmd, item, insertFields));
    }
}

}

OrmLiteWriteCommandExtensions

internal static int InsertMultiValues(this IDbCommand dbCmd, Action commandFilter, params T[] objs) { IDbTransaction dbTrans = null; int execCount = 0; try { if (dbCmd.Transaction == null) dbCmd.Transaction = dbTrans = dbCmd.Connection.BeginTransaction();

            var dialectProvider = dbCmd.GetDialectProvider();
            dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd);

            var splitStr = " VALUES ";
            var valuesIndexOf = dbCmd.CommandText.IndexOf(splitStr);
            var insertIntoPrefix = dbCmd.CommandText.Substring(0, valuesIndexOf);
            var valuesText = dbCmd.CommandText.Substring(valuesIndexOf + splitStr.Length);

            var modelDef = typeof(T).GetModelDefinition();
            var fieldMap = dialectProvider.GetFieldDefinitionMap(modelDef);
            var fieldDefs = modelDef.FieldDefinitionsArray;

            commandFilter?.Invoke(dbCmd);
            string commandTextValues = " ";
            int index = 0;

            dbCmd.Parameters.Clear();
            foreach (var obj in objs)
            {
                var paramPostfix = $"_{index}";  //参数下标后缀
                var curValuesText = valuesText.Replace(",", $"{paramPostfix},");
                curValuesText = curValuesText.Replace(")", $"{paramPostfix})");
                commandTextValues += curValuesText;
                if (index != objs.Length - 1)
                {
                    commandTextValues += ", ";
                }
                foreach (var fieldDef in fieldDefs)
                {
                    var p = dbCmd.CreateParameter();
                    dialectProvider.SetParameterValue<T>(fieldDef, p, obj);
                    p.ParameterName = $"{dialectProvider.GetParam(dialectProvider.SanitizeFieldNameForParamName(fieldDef.FieldName))}{paramPostfix}";
                    dbCmd.Parameters.Add(p);
                }
                index++;
            }
            dbCmd.CommandText = insertIntoPrefix + splitStr + commandTextValues;

            execCount = dbCmd.ExecNonQuery();

            dbTrans?.Commit();
        }
        finally
        {
            dbTrans?.Dispose();
        }

        return execCount;
    }

internal static int InsertMultiValues(this IDbCommand dbCmd, Action commandFilter, params T[] objs) { IDbTransaction dbTrans = null; int execCount = 0; try { if (dbCmd.Transaction == null) dbCmd.Transaction = dbTrans = dbCmd.Connection.BeginTransaction();

            var dialectProvider = dbCmd.GetDialectProvider();
            dialectProvider.PrepareParameterizedInsertStatement<T>(dbCmd);

            var splitStr = " VALUES ";
            var valuesIndexOf = dbCmd.CommandText.IndexOf(splitStr);
            var insertIntoPrefix = dbCmd.CommandText.Substring(0, valuesIndexOf);
            var valuesText = dbCmd.CommandText.Substring(valuesIndexOf + splitStr.Length);
            valuesText = valuesText.Replace("(", "").Replace(")", "");
            var valuesArray = valuesText.Split(',');

            var modelDef = typeof(T).GetModelDefinition();
            var fieldMap = dialectProvider.GetFieldDefinitionMap(modelDef);
            var fieldDefs = modelDef.FieldDefinitionsArray;

            commandFilter?.Invoke(dbCmd);
            string commandTextValues = " ";
            int index = 0;

            dbCmd.Parameters.Clear();
            foreach (var obj in objs)
            {
                var paramPostfix = $"_{index}";  //参数下标后缀
                commandTextValues += $"({valuesArray.Join($"{paramPostfix},")}{paramPostfix})";
                //var curValuesText = valuesText.Replace(",", $"{paramPostfix},");
                //curValuesText = curValuesText.Replace(")", $"{paramPostfix})");
                if (index != objs.Length - 1)
                {
                    commandTextValues += ", ";
                }
                foreach (var fieldDef in fieldDefs)
                {
                    var p = dbCmd.CreateParameter();
                    dialectProvider.SetParameterValue<T>(fieldDef, p, obj);
                    p.ParameterName = $"{dialectProvider.GetParam(dialectProvider.SanitizeFieldNameForParamName(fieldDef.FieldName))}{paramPostfix}";
                    dbCmd.Parameters.Add(p);
                }
                index++;
            }
            dbCmd.CommandText = $"{insertIntoPrefix}{splitStr}{commandTextValues}";

            execCount = dbCmd.ExecNonQuery();

            dbTrans?.Commit();
        }
        finally
        {
            dbTrans?.Dispose();
        }

        return execCount;
    }
⚠️ **GitHub.com Fallback** ⚠️