using FreeSql; using FreeSql.Internal.Model; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using FreeSql.Internal.CommonProvider; #if microsoft using Microsoft.Data.SqlClient; #else using System.Data.SqlClient; #endif using System.Threading.Tasks; public static partial class FreeSqlSqlServerGlobalExtensions { /// /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换 /// /// /// /// public static string FormatSqlServer(this string that, params object[] args) => _sqlserverAdo.Addslashes(that, args); static FreeSql.SqlServer.SqlServerAdo _sqlserverAdo = new FreeSql.SqlServer.SqlServerAdo(); /// /// SqlServer with(nolock) 查询 /// /// /// /// /// 多表查询时的锁规则 /// public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class => LocalWithLock(that, lockType, rule); public static ISelect WithLock(this ISelect that, SqlServerLock lockType = SqlServerLock.NoLock, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class => LocalWithLock(that, lockType, rule); static TReturn LocalWithLock(TReturn query, SqlServerLock lockType, Dictionary rule) { var selectProvider = query as Select0Provider; var oldalias = selectProvider._aliasRule; selectProvider._aliasRule = (type, old) => { if (oldalias != null) old = oldalias(type, old); if (rule == null) return LocalAppendWithString(old, lockType.ToString()); return rule.TryGetValue(type, out var trybool) && trybool ? LocalAppendWithString(old, lockType.ToString()) : old; }; return query; } /// /// SqkServer with(index) 强制索引 /// /// /// /// /// public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class => LocalWithIndex(that, indexName, rule); public static ISelect WithIndex(this ISelect that, string indexName, Dictionary rule = null) where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class => LocalWithIndex(that, indexName, rule); static TReturn LocalWithIndex(TReturn query, string indexName, Dictionary rule) { if (string.IsNullOrWhiteSpace(indexName)) return query; var selectProvider = query as Select0Provider; var oldalias = selectProvider._aliasRule; selectProvider._aliasRule = (type, old) => { if (oldalias != null) old = oldalias(type, old); if (type == selectProvider._tables[0].Table.Type) return LocalAppendWithString(old, $"index={indexName}"); if (rule == null) return old; return rule.TryGetValue(type, out var tryidxName) && string.IsNullOrWhiteSpace(tryidxName) == false ? LocalAppendWithString(old, $"index={tryidxName}") : old; }; return query; } static string LocalAppendWithString(string old, string str) => old?.Contains(" With(") == true ? old.Replace(" With(", $" With({str}, ") : $"{old} With({str})"; /// /// 设置全局 SqlServer with(nolock) 查询 /// /// /// public static IFreeSql SetGlobalSelectWithLock(this IFreeSql that, SqlServerLock lockType, Dictionary rule) { var value = NativeTuple.Create(lockType, rule); _dicSetGlobalSelectWithLock.AddOrUpdate(that.Ado.Identifier, value, (_, __) => value); return that; } internal static ConcurrentDictionary>> _dicSetGlobalSelectWithLock = new ConcurrentDictionary>>(); #region ExecuteSqlBulkCopy /// /// SqlServer SqlCopyBulk 批量插入功能 /// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列 /// 使用 WithConnection/WithTransaction 传入连接/事务对象 /// 提示:若本方法不能满足,请使用 IInsert<T>.ToDataTable 方法得到 DataTable 对象后,自行处理。 /// SqlCopyBulk 与 insert into t values(..),(..),(..) 性能测试参考: /// 插入180000行,52列:21,065ms 与 402,355ms,10列:4,248ms 与 47,204ms /// 插入10000行,52列:578ms 与 24,847ms,10列:127ms 与 2,275ms /// 插入5000行,52列:326ms 与 11,465ms,10列:71ms 与 1,108ms /// 插入2000行,52列:139ms 与 4,971ms,10列:30ms 与 488ms /// 插入1000行,52列:105ms 与 2,437ms,10列:48ms 与 279ms /// 插入500行,52列:79ms 与 915ms,10列:14ms 与 123ms /// 插入100行,52列:60ms 与 138ms,10列:11ms 与 35ms /// 插入50行,52列:48ms 与 88ms,10列:10ms 与 16ms /// /// /// /// /// /// public static void ExecuteSqlBulkCopy(this IInsert that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null) where T : class { var insert = that as FreeSql.SqlServer.Curd.SqlServerInsert; if (insert == null) throw new Exception(CoreStrings.S_Features_Unique("ExecuteSqlBulkCopy", "SqlServer")); var dt = that.ToDataTable(); if (dt.Rows.Count == 0) return; Action writeToServer = bulkCopy => { if (batchSize.HasValue) bulkCopy.BatchSize = batchSize.Value; if (bulkCopyTimeout.HasValue) bulkCopy.BulkCopyTimeout = bulkCopyTimeout.Value; bulkCopy.DestinationTableName = dt.TableName; for (int i = 0; i < dt.Columns.Count; i++) bulkCopy.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName); bulkCopy.WriteToServer(dt); }; try { if (insert.InternalConnection == null && insert.InternalTransaction == null) { if (insert._orm.Ado?.TransactionCurrentThread != null) using (var bulkCopy = new SqlBulkCopy(insert._orm.Ado.TransactionCurrentThread.Connection as SqlConnection, copyOptions, insert._orm.Ado.TransactionCurrentThread as SqlTransaction)) writeToServer(bulkCopy); else using (var conn = insert.InternalOrm.Ado.MasterPool.Get()) { using (var bulkCopy = copyOptions == SqlBulkCopyOptions.Default ? new SqlBulkCopy(conn.Value as SqlConnection) : new SqlBulkCopy(conn.Value as SqlConnection, copyOptions, null)) { writeToServer(bulkCopy); } } } else if (insert.InternalTransaction != null) { using (var bulkCopy = new SqlBulkCopy(insert.InternalTransaction.Connection as SqlConnection, copyOptions, insert.InternalTransaction as SqlTransaction)) { writeToServer(bulkCopy); } } else if (insert.InternalConnection != null) { var conn = insert.InternalConnection as SqlConnection; var isNotOpen = false; if (conn.State != System.Data.ConnectionState.Open) { isNotOpen = true; conn.Open(); } try { using (var bulkCopy = copyOptions == SqlBulkCopyOptions.Default ? new SqlBulkCopy(conn) : new SqlBulkCopy(conn, copyOptions, null)) { writeToServer(bulkCopy); } } finally { if (isNotOpen) conn.Close(); } } else { throw new NotImplementedException($"ExecuteSqlBulkCopy {CoreStrings.S_Not_Implemented_FeedBack}"); } } finally { dt.Clear(); } } #if net40 #else async public static Task ExecuteSqlBulkCopyAsync(this IInsert that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class { var insert = that as FreeSql.SqlServer.Curd.SqlServerInsert; if (insert == null) throw new Exception(CoreStrings.S_Features_Unique("ExecuteSqlBulkCopyAsync", "SqlServer")); var dt = that.ToDataTable(); if (dt.Rows.Count == 0) return; Func writeToServerAsync = bulkCopy => { if (batchSize.HasValue) bulkCopy.BatchSize = batchSize.Value; if (bulkCopyTimeout.HasValue) bulkCopy.BulkCopyTimeout = bulkCopyTimeout.Value; bulkCopy.DestinationTableName = dt.TableName; for (int i = 0; i < dt.Columns.Count; i++) bulkCopy.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName); return bulkCopy.WriteToServerAsync(dt, cancellationToken); }; try { if (insert.InternalConnection == null && insert.InternalTransaction == null) { if (insert._orm.Ado?.TransactionCurrentThread != null) using (var bulkCopy = new SqlBulkCopy(insert._orm.Ado.TransactionCurrentThread.Connection as SqlConnection, copyOptions, insert._orm.Ado.TransactionCurrentThread as SqlTransaction)) await writeToServerAsync(bulkCopy); else using (var conn = await insert.InternalOrm.Ado.MasterPool.GetAsync()) { using (var bulkCopy = copyOptions == SqlBulkCopyOptions.Default ? new SqlBulkCopy(conn.Value as SqlConnection) : new SqlBulkCopy(conn.Value as SqlConnection, copyOptions, null)) { await writeToServerAsync(bulkCopy); } } } else if (insert.InternalTransaction != null) { using (var bulkCopy = new SqlBulkCopy(insert.InternalTransaction.Connection as SqlConnection, copyOptions, insert.InternalTransaction as SqlTransaction)) { await writeToServerAsync(bulkCopy); } } else if (insert.InternalConnection != null) { var conn = insert.InternalConnection as SqlConnection; var isNotOpen = false; if (conn.State != System.Data.ConnectionState.Open) { isNotOpen = true; await conn.OpenAsync(cancellationToken); } try { using (var bulkCopy = copyOptions == SqlBulkCopyOptions.Default ? new SqlBulkCopy(conn) : new SqlBulkCopy(conn, copyOptions, null)) { await writeToServerAsync(bulkCopy); } } finally { if (isNotOpen) conn.Close(); } } else { throw new NotImplementedException($"ExecuteSqlBulkCopyAsync {CoreStrings.S_Not_Implemented_FeedBack}"); } } finally { dt.Clear(); } } #endif #endregion } [Flags] public enum SqlServerLock { NoLock = 1, HoldLock = 2, UpdLock = 4, RowLock = 8, ReadCommitted = 16, ReadPast = 32, ReadUnCommitted = 64, RepeaTableRead = 256, PagLock = 512, Serializable = 1024, TabLock = 2048, TabLockX = 4096, XLock = 8192, NoWait = 16384 }