diff --git a/Examples/base_entity/Program.cs b/Examples/base_entity/Program.cs index f231a775..31c96e9e 100644 --- a/Examples/base_entity/Program.cs +++ b/Examples/base_entity/Program.cs @@ -6,8 +6,6 @@ using FreeSql.Internal; using FreeSql.Internal.CommonProvider; using FreeSql.Internal.Model; using FreeSql.Odbc.Default; -using K4os.Hash.xxHash; -using MySqlX.XDevAPI; using NetTopologySuite.Geometries; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -541,13 +539,13 @@ namespace base_entity .UseConnectionString(FreeSql.DataType.Firebird, @"database=localhost:D:\fbdata\EXAMPLES.fdb;user=sysdba;password=123456;max pool size=5") //.UseQuoteSqlName(false) - //.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;min pool size=1;Max pool size=2") + .UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;min pool size=1;Max pool size=2;AllowLoadLocalInfile=true") - .UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=3;TrustServerCertificate=true") + //.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=3;TrustServerCertificate=true") - .UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=2") + //.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=2") //.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=toc;Pooling=true;Maximum Pool Size=2") - .UseNameConvert(FreeSql.Internal.NameConvertType.ToLower) + //.UseNameConvert(FreeSql.Internal.NameConvertType.ToLower) //.UseConnectionString(FreeSql.DataType.Oracle, "user id=user1;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=2") //.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper) @@ -586,7 +584,7 @@ namespace base_entity fsql.InsertOrUpdate() .SetSource(fsql.Select().ToList()) - .ExecutePgCopy(); + .ExecuteMySqlBulkCopy(); var updatejoin01 = fsql.Update() .Join(fsql.Select(), (a, b) => a.GroupId == b.Id) diff --git a/Examples/base_entity/base_entity.csproj b/Examples/base_entity/base_entity.csproj index f88e6417..77d8a921 100644 --- a/Examples/base_entity/base_entity.csproj +++ b/Examples/base_entity/base_entity.csproj @@ -27,7 +27,7 @@ - + diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 594fbad3..26522f10 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -733,6 +733,15 @@ + + + 根据Assembly扫描所有继承IEntityTypeConfiguration<T>的配置类 + + + + + + 创建普通数据上下文档对象 @@ -791,5 +800,14 @@ + + + 批量注入 Repository,可以参考代码自行调整 + + + + + + diff --git a/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs b/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs index 0cbb6324..4c33a1b2 100644 --- a/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs +++ b/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs @@ -18,7 +18,57 @@ using MySql.Data.MySqlClient; public static class FreeSqlMySqlConnectorGlobalExtensions { #region ExecuteMySqlBulkCopy - + /// + /// 批量插入或更新(操作的字段数量超过 2000 时收益大) + /// 实现原理:使用 MySqlBulkCopy 插入临时表,再执行 INSERT INTO t1 select * from #temp ON DUPLICATE KEY UPDATE ... + /// + /// + /// + /// + /// + public static int ExecuteMySqlBulkCopy(this IInsertOrUpdate that, int? bulkCopyTimeout = null) where T : class + { + var upsert = that as InsertOrUpdateProvider; + if (upsert._source.Any() != true || upsert._tempPrimarys.Any() == false) return 0; + var state = ExecuteMySqlBulkCopyState(upsert); + return UpdateProvider.ExecuteBulkUpsert(upsert, state, insert => insert.ExecuteMySqlBulkCopy(bulkCopyTimeout)); + } + static NativeTuple ExecuteMySqlBulkCopyState(InsertOrUpdateProvider upsert) where T : class + { + if (upsert._source.Any() != true) return null; + var _table = upsert._table; + var _commonUtils = upsert._commonUtils; + var updateTableName = upsert._tableRule?.Invoke(_table.DbName) ?? _table.DbName; + var tempTableName = $"Temp_{Guid.NewGuid().ToString("N")}"; + if (upsert._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower(); + if (upsert._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper(); + if (upsert._connection == null && upsert._orm.Ado.TransactionCurrentThread != null) + upsert.WithTransaction(upsert._orm.Ado.TransactionCurrentThread); + var sb = new StringBuilder().Append("CREATE TEMPORARY TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( "); + foreach (var col in _table.Columns.Values) + { + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" ").Append(col.Attribute.DbType.Replace("NOT NULL", "")); + sb.Append(","); + } + var sql1 = sb.Remove(sb.Length - 1, 1).Append(" \r\n) Engine=InnoDB;").ToString(); + try + { + upsert._sourceSql = $"select __**__ from {tempTableName}"; + var sql2 = upsert.ToSql(); + if (string.IsNullOrWhiteSpace(sql2) == false) + { + var field = sql2.Substring(sql2.IndexOf("`(") + 2); + field = field.Remove(field.IndexOf(upsert._sourceSql)).TrimEnd().TrimEnd(')'); + sql2 = sql2.Replace(upsert._sourceSql, $"select {field} from {tempTableName}"); + } + var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}"; + return NativeTuple.Create(sql1, sql2, sql3, tempTableName, _table.Columns.Values.Select(a => a.Attribute.Name).ToArray()); + } + finally + { + upsert._sourceSql = null; + } + } /// /// 批量更新(更新字段数量超过 2000 时收益大) /// 实现原理:使用 MySqlBulkCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新 @@ -147,6 +197,13 @@ public static class FreeSqlMySqlConnectorGlobalExtensions } #if net40 #else + public static Task ExecuteMySqlBulkCopyAsync(this IInsertOrUpdate that, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class + { + var upsert = that as UpdateProvider; + if (upsert._source.Any() != true || upsert._tempPrimarys.Any() == false) return Task.FromResult(0); + var state = ExecuteMySqlBulkCopyState(upsert); + return UpdateProvider.ExecuteBulkUpdateAsync(upsert, state, insert => insert.ExecuteMySqlBulkCopyAsync(bulkCopyTimeout, cancellationToken)); + } public static Task ExecuteMySqlBulkCopyAsync(this IUpdate that, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class { var update = that as UpdateProvider; diff --git a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs index 1605cd97..d67a006b 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs +++ b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs @@ -71,8 +71,14 @@ public static partial class FreeSqlPostgreSQLGlobalExtensions sb.Clear(); try { - upsert._sourceSql = $"select * from {tempTableName}"; + upsert._sourceSql = $"select __**__ from {tempTableName}"; var sql2 = upsert.ToSql(); + if (string.IsNullOrWhiteSpace(sql2) == false) + { + var field = sql2.Substring(sql2.IndexOf("\"(") + 2); + field = field.Remove(field.IndexOf(upsert._sourceSql)).TrimEnd().TrimEnd(')'); + sql2 = sql2.Replace(upsert._sourceSql, $"select {field} from {tempTableName}"); + } var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}"; return NativeTuple.Create(sql1, sql2, sql3, tempTableName, _table.Columns.Values.Select(a => a.Attribute.Name).ToArray()); }