diff --git a/Directory.Build.props b/Directory.Build.props index 37a6ecf5..55ad4bcb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,7 @@ diff --git a/Examples/base_entity/Entities/User.cs b/Examples/base_entity/Entities/User.cs index 3b8270ec..faf9a460 100644 --- a/Examples/base_entity/Entities/User.cs +++ b/Examples/base_entity/Entities/User.cs @@ -1,4 +1,5 @@ using FreeSql; +using FreeSql.DataAnnotations; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -57,3 +58,11 @@ public class User1 : BaseEntity [MaxLength(4000)] public string Description { get; set; } } + +public class IdentityTable +{ + [Column(IsIdentity = true, IsPrimary = true)] + public int id { get; set; } + + public string name { get; set; } +} \ No newline at end of file diff --git a/Examples/base_entity/Program.cs b/Examples/base_entity/Program.cs index ba879bbc..0adbd60a 100644 --- a/Examples/base_entity/Program.cs +++ b/Examples/base_entity/Program.cs @@ -451,10 +451,10 @@ namespace base_entity //.UseSlaveWeight(10, 1, 1, 5) - //.UseConnectionString(FreeSql.DataType.Firebird, @"database=localhost:D:\fbdata\EXAMPLES.fdb;user=sysdba;password=123456;max pool size=5") + .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") //.UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=freesqlTest;Pooling=true;Max Pool Size=3;TrustServerCertificate=true") @@ -480,13 +480,52 @@ namespace base_entity //.UseConnectionString(FreeSql.DataType.OdbcDameng, "Driver={DM8 ODBC DRIVER};Server=127.0.0.1:5236;Persist Security Info=False;Trusted_Connection=Yes;UID=USER1;PWD=123456789") - .UseMonitorCommand(cmd => Console.WriteLine(cmd.CommandText + "\r\n")) + .UseMonitorCommand(cmd => + { + Console.WriteLine(cmd.CommandText + "\r\n"); + cmd.CommandText = null; //不执行 + }) .UseLazyLoading(true) //.UseGenerateCommandParameterWithLambda(true) .Build(); BaseEntity.Initialization(fsql, () => _asyncUow.Value); #endregion + var groupsql01 = fsql.Select() + .GroupBy(a => new + { + djjg = a.Id, + qllx = a.Nickname, + hjhs = a.GroupId + }) + .ToSql(g => new + { + g.Key.djjg, + g.Key.qllx, + xhjsl = g.Count(g.Key.djjg), + hjzhs = g.Sum(g.Key.hjhs), + blywsl = g.Count() + }, FieldAliasOptions.AsProperty); + + using (var uow = fsql.CreateUnitOfWork()) + { + uow.Orm.Select().ForUpdate().ToList(); + } + + var listaaaddd = new List(); + for (int i = 0; i < 2; i++) + { + listaaaddd.Add(new User1 { Nickname = $"测试文本:{i}" }); + } + fsql.Select(); + fsql.Transaction(() => + { + + fsql.Insert(listaaaddd).ExecuteAffrows(); //加在事务里就出错 + }); + + fsql.Select().Count(); + var dkdksql = fsql.Select().WithLock().From() .InnerJoin((user, usergroup) => user.GroupId == usergroup.Id && usergroup.GroupName == "xxx") .ToSql(); diff --git a/Examples/orm_vs/Program.cs b/Examples/orm_vs/Program.cs index 10facd16..cd15bb85 100644 --- a/Examples/orm_vs/Program.cs +++ b/Examples/orm_vs/Program.cs @@ -15,15 +15,16 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using FreeSql; +using FreeSql.Internal.CommonProvider; namespace orm_vs { class Program { static IFreeSql fsql = new FreeSql.FreeSqlBuilder() - .UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=tedb;Pooling=true;Max Pool Size=20") - //.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=20") - //.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=20") + .UseConnectionString(FreeSql.DataType.SqlServer, "Data Source=.;Integrated Security=True;Initial Catalog=tedb1;Pooling=true;Max Pool Size=21;TrustServerCertificate=true") + //.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=21;AllowLoadLocalInfile=true;") + //.UseConnectionString(FreeSql.DataType.PostgreSQL, "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=21") .UseAutoSyncStructure(false) .UseNoneCommandParameter(true) //.UseConfigEntityFromDbFirst(true) @@ -35,11 +36,11 @@ namespace orm_vs { var db = new SqlSugarClient(new ConnectionConfig() { - ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=tedb;Pooling=true;Min Pool Size=20;Max Pool Size=20", + ConnectionString = "Data Source=.;Integrated Security=True;Initial Catalog=tedb1;Pooling=true;Min Pool Size=20;Max Pool Size=20;TrustServerCertificate=true", DbType = DbType.SqlServer, - //ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20", + //ConnectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=20;Max Pool Size=20;AllowLoadLocalInfile=true;", //DbType = DbType.MySql, - //ConnectionString = "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=21", + //ConnectionString = "Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=20", //DbType = DbType.PostgreSQL, IsAutoCloseConnection = true, InitKeyType = InitKeyType.Attribute @@ -58,18 +59,15 @@ namespace orm_vs public DbSet PatientExamination_2022s { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseSqlServer(@"Data Source=.;Integrated Security=True;Initial Catalog=tedb;Pooling=true;Min Pool Size=21;Max Pool Size=21"); - //optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21"); - //optionsBuilder.UseNpgsql("Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=21"); + optionsBuilder.UseSqlServer(@"Data Source=.;Integrated Security=True;Initial Catalog=tedb1;Pooling=true;Min Pool Size=19;Max Pool Size=19;TrustServerCertificate=true"); + //var connectionString = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=19;Max Pool Size=19"; + //optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); + //optionsBuilder.UseNpgsql("Host=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Maximum Pool Size=19"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - - modelBuilder.Entity() - .Property(a => a.create_time) - .HasConversion(a => int.Parse(a.ToString()), a => new DateTime(a)); } } @@ -136,7 +134,7 @@ namespace orm_vs } static void TestDapperSelectPatientExamination_2022() { - using (var conn = new SqlConnection("Data Source=.;Integrated Security=True;Initial Catalog=tedb;Pooling=true;Min Pool Size=21;Max Pool Size=22")) + using (var conn = new SqlConnection("Data Source=.;Integrated Security=True;Initial Catalog=tedb1;Pooling=true;Min Pool Size=21;Max Pool Size=22")) { var list = conn.Query("select top 40000 * from PatientExamination_2022"); } @@ -145,28 +143,28 @@ namespace orm_vs static DbConnection fsqlConn = null; static void Main(string[] args) { - var count = 0; - var sws = new List(); - Console.WriteLine("观察查询4万条记录内存,按 Enter 进入下一次,按任易键即出程序。。。"); - //while(Console.ReadKey().Key == ConsoleKey.Enter) - //using (var fcon = fsql.Ado.MasterPool.Get()) - //{ - //fsqlConn = fcon.Value; - for (var a = 0; a < 80; a++) - { - Stopwatch sw = Stopwatch.StartNew(); - TestFreeSqlSelectPatientExamination_2022(); - //TestEfSelectPatientExamination_2022(); - //TestSqlSugarSelectPatientExamination_2022(); - //TestDapperSelectPatientExamination_2022(); - sw.Stop(); - sws.Add(sw.ElapsedMilliseconds); - Console.WriteLine($"第 {++count} 次,查询4万条记录, {sw.ElapsedMilliseconds}ms,平均 {(long)sws.Average()}ms"); - } - //} - Console.ReadKey(); - fsql.Dispose(); - return; + //var count = 0; + //var sws = new List(); + //Console.WriteLine("观察查询4万条记录内存,按 Enter 进入下一次,按任易键即出程序。。。"); + ////while(Console.ReadKey().Key == ConsoleKey.Enter) + ////using (var fcon = fsql.Ado.MasterPool.Get()) + ////{ + // //fsqlConn = fcon.Value; + // for (var a = 0; a < 80; a++) + // { + // Stopwatch sw = Stopwatch.StartNew(); + // TestFreeSqlSelectPatientExamination_2022(); + // //TestEfSelectPatientExamination_2022(); + // //TestSqlSugarSelectPatientExamination_2022(); + // //TestDapperSelectPatientExamination_2022(); + // sw.Stop(); + // sws.Add(sw.ElapsedMilliseconds); + // Console.WriteLine($"第 {++count} 次,查询4万条记录, {sw.ElapsedMilliseconds}ms,平均 {(long)sws.Average()}ms"); + // } + ////} + //Console.ReadKey(); + //fsql.Dispose(); + //return; //fsql.CodeFirst.SyncStructure(typeof(Song), typeof(Song_tag), typeof(Tag)); //sugar.CodeFirst.InitTables(typeof(Song), typeof(Song_tag), typeof(Tag)); @@ -186,13 +184,6 @@ namespace orm_vs var sb = new StringBuilder(); var time = new Stopwatch(); - var sql222 = fsql.Select().Where(a => DateTime.Now.Subtract(a.create_time.Value).TotalHours > 0).ToSql(); - - var conModels = new List(); - conModels.Add(new ConditionalModel { FieldName = "`id` = 1 or 1=1; delete from song_tag; -- ", ConditionalType = ConditionalType.Equal, FieldValue = "1" }); - - var student = sugar.Queryable().Where(conModels).ToList(); - #region ET test ////var t31 = fsql.Select().ToList(); @@ -483,23 +474,10 @@ namespace orm_vs #endregion - var testlist1 = fsql.Select().OrderBy(a => a.id).ToList(); - var testlist2 = new List(); - fsql.Select().OrderBy(a => a.id).ToChunk(2, fetch => + sugar.Aop.OnLogExecuted = (s, e) => { - testlist2.AddRange(fetch.Object); - }); - - var testlist22 = new List(); - fsql.Select().LeftJoin((a, b) => a.id == b.song_id).ToChunk((a, b) => new { a.title, a.create_time, b.tag_id }, 2, fetch => - { - testlist22.AddRange(fetch.Object); - }); - - //sugar.Aop.OnLogExecuted = (s, e) => - //{ - // Trace.WriteLine(s); - //}; + Trace.WriteLine(s); + }; //测试前清空数据 fsql.Delete().Where(a => a.id > 0).ExecuteAffrows(); sugar.Deleteable().Where(a => a.id > 0).ExecuteCommand(); @@ -548,10 +526,10 @@ namespace orm_vs sb.Clear(); Console.WriteLine("更新:"); - Update(sb, 100, 1); + Update(sb, 10, 1); Console.Write(sb.ToString()); sb.Clear(); - Update(sb, 100, 10); + Update(sb, 10, 10); Console.Write(sb.ToString()); sb.Clear(); @@ -598,21 +576,21 @@ namespace orm_vs sw.Stop(); sb.AppendLine($"EFCore Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms .net5.0无效"); - sw.Restart(); - using (var conn = fsql.Ado.MasterPool.Get()) - { - for (var a = 0; a < forTime; a++) - Dapper.SqlMapper.Query(conn.Value, $"select top {size} * from freesql_song").ToList(); - } - sw.Stop(); - sb.AppendLine($"Dapper Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); + //sw.Restart(); + //using (var conn = fsql.Ado.MasterPool.Get()) + //{ + // for (var a = 0; a < forTime; a++) + // Dapper.SqlMapper.Query(conn.Value, $"select * from freesql_song limit {size}").ToList(); + //} + //sw.Stop(); + //sb.AppendLine($"Dapper Select {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); } static void Insert(StringBuilder sb, int forTime, int size) { var songs = Enumerable.Range(0, size).Select(a => new Song { - create_time = DateTime.Now, + create_time = DateTime.Now.ToString(), is_deleted = false, title = $"Insert_{a}", url = $"Url_{a}" @@ -647,6 +625,7 @@ namespace orm_vs try { for (var a = 0; a < forTime; a++) + //sugar.Fastest().BulkCopy(songs.ToList()); sugar.Insertable(songs.ToArray()).ExecuteCommand(); } catch (Exception ex) @@ -682,7 +661,61 @@ namespace orm_vs fsql.Update().SetSource(songs).ExecuteAffrows(); } sw.Stop(); - sb.AppendLine($"FreeSql Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); + sb.AppendLine($"FreeSql Update1 {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); + + + songs = fsql.Select().Limit(size).ToList(); + sw.Restart(); + for (var a = 0; a < forTime; a++) + { + fsql.Update().SetSource(songs).ExecuteSqlBulkCopy(); + } + sw.Stop(); + sb.AppendLine($"FreeSql BulkCopyUpdate {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); + + // songs = fsql.Select().Limit(size).ToList(); + // sw.Restart(); + // for (var a = 0; a < forTime; a++) + // { + // //fsql.Update().SetSource(songs).ExecuteAffrows(); + // var iou = fsql.InsertOrUpdate() as InsertOrUpdateProvider; + // var dbsql = new StringBuilder(); + // var dbparms = new List(); + // iou.WriteSourceSelectUnionAll(songs, dbsql, dbparms); + + // var sql = $@"update freesql_song a + //inner join ( {dbsql} ) b on b.id = a.id + //set a.create_time = b.create_time, a.is_deleted = b.is_deleted, a.title = b.title, a.url = b.url"; + // fsql.Ado.ExecuteNonQuery(System.Data.CommandType.Text, sql, dbparms.ToArray()); + // } + // sw.Stop(); + // sb.AppendLine($"FreeSql Update2(update inner join) {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); + + // songs = fsql.Select().Limit(size).ToList(); + // sw.Restart(); + // for (var a = 0; a < forTime; a++) + // { + // var isdroped = false; + // var tempTableName = $"#Temp_freesql_song"; + // fsql.Ado.ExecuteNonQuery($"select * into {tempTableName} from [freesql_song] where 1=2"); + // try + // { + // fsql.Insert(songs).AsTable(tempTableName).ExecuteMySqlBulkCopy(); + // var sql = $@"update freesql_song a + //inner join {tempTableName} b on b.id = a.id; + //set a.create_time = b.create_time, a.is_deleted = b.is_deleted, a.title = b.title, a.url = b.url + //; drop table {tempTableName}; "; + // fsql.Ado.ExecuteNonQuery(System.Data.CommandType.Text, sql); + // isdroped = true; + // } + // finally + // { + // if (isdroped == false) + // fsql.Ado.ExecuteNonQuery($"drop table {tempTableName}"); + // } + // } + // sw.Stop(); + // sb.AppendLine($"FreeSql Update3(update inner join #temp) {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); songs = sugar.Queryable().Take(size).ToList(); sw.Restart(); @@ -690,7 +723,7 @@ namespace orm_vs try { for (var a = 0; a < forTime; a++) - sugar.Updateable(songs).ExecuteCommand(); + sugar.Fastest().BulkUpdate(songs); } catch (Exception ex) { @@ -699,21 +732,21 @@ namespace orm_vs sw.Stop(); sb.AppendLine($"SqlSugar Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : "")); - using (var db = new SongContext()) - { - songs = db.Songs.Take(size).AsNoTracking().ToList(); - } - sw.Restart(); - for (var a = 0; a < forTime; a++) - { + //using (var db = new SongContext()) + //{ + // songs = db.Songs.Take(size).AsNoTracking().ToList(); + //} + //sw.Restart(); + //for (var a = 0; a < forTime; a++) + //{ - using (var db = new SongContext()) - { - //db.Configuration.AutoDetectChangesEnabled = false; - //db.Songs.UpdateRange(songs.ToArray()); - //db.SaveChanges(); - } - } + // using (var db = new SongContext()) + // { + // //db.Configuration.AutoDetectChangesEnabled = false; + // //db.Songs.UpdateRange(songs.ToArray()); + // //db.SaveChanges(); + // } + //} sw.Stop(); sb.AppendLine($"EFCore Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms .net5.0无效\r\n"); } @@ -729,7 +762,7 @@ namespace orm_vs [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int id { get; set; } - public DateTime? create_time { get; set; } + public string create_time { get; set; } public bool? is_deleted { get; set; } public string title { get; set; } public string url { get; set; } diff --git a/Examples/orm_vs/orm_vs.csproj b/Examples/orm_vs/orm_vs.csproj index 08ab5687..e3ef045b 100644 --- a/Examples/orm_vs/orm_vs.csproj +++ b/Examples/orm_vs/orm_vs.csproj @@ -2,20 +2,20 @@ Exe - net5.0 + net7.0 - - - - - + + + + + - + diff --git a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj index e5d57e87..fac5ba51 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj +++ b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj @@ -19,7 +19,7 @@ key.snk false latest - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj b/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj index 07afeafb..e4dfe72a 100644 --- a/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj +++ b/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj b/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj index fdad0bc7..9f5d9bf0 100644 --- a/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj +++ b/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj index c277521f..84211ac8 100644 --- a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj +++ b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Extensions/FreeSql.Generator/ConsoleApp.cs b/Extensions/FreeSql.Generator/ConsoleApp.cs index 92530b50..50050d10 100644 --- a/Extensions/FreeSql.Generator/ConsoleApp.cs +++ b/Extensions/FreeSql.Generator/ConsoleApp.cs @@ -22,6 +22,7 @@ namespace FreeSql.Generator string ArgsConnectionString { get; } string ArgsFilter { get; } string ArgsMatch { get; } + string ArgsJson { get; } string ArgsFileName { get; } bool ArgsReadKey { get; } internal string ArgsOutput { get; private set; } @@ -62,6 +63,7 @@ new Colorful.Formatter("v" + string.Join(".", typeof(ConsoleApp).Assembly.GetNam ArgsNameSpace = "MyProject"; ArgsFilter = ""; ArgsMatch = ""; + ArgsJson = "Newtonsoft.Json"; ArgsFileName = "{name}.cs"; ArgsReadKey = true; Action setArgsOutput = value => @@ -94,7 +96,7 @@ new Colorful.Formatter("v" + string.Join(".", typeof(ConsoleApp).Assembly.GetNam -NameOptions * 4个布尔值对应: 首字母大写 - 首字母大写,其他小写 + 首字母大写,其他小写 全部小写 下划线转驼峰 @@ -116,6 +118,8 @@ new Colorful.Formatter("v" + string.Join(".", typeof(ConsoleApp).Assembly.GetNam 如果不想生成视图和存储过程 -Filter View+StoreProcedure -Match 表名或正则表达式,只生成匹配的表,如:dbo\.TB_.+ + -Json NTJ、STJ、NONE + Newtonsoft.Json、System.Text.Json、不生成 -FileName 文件名,默认:{name}.cs -Output 保存路径,默认为当前 shell 所在目录 @@ -196,6 +200,18 @@ new Colorful.Formatter("推荐在实体类目录创建 gen.bat,双击它重新 if (Regex.IsMatch("", ArgsMatch)) { } //throw a++; break; + case "-json": + switch(args[a + 1].Trim().ToLower()) + { + case "none": + ArgsJson = ""; + break; + case "stj": + ArgsJson = "System.Text.Json"; + break; + } + a++; + break; case "-filename": ArgsFileName = args[a + 1]; a++; diff --git a/Extensions/FreeSql.Generator/FreeSql.Generator.csproj b/Extensions/FreeSql.Generator/FreeSql.Generator.csproj index 4290b3f7..0ea57f7f 100644 --- a/Extensions/FreeSql.Generator/FreeSql.Generator.csproj +++ b/Extensions/FreeSql.Generator/FreeSql.Generator.csproj @@ -13,7 +13,7 @@ https://github.com/2881099/FreeSql https://github.com/2881099/FreeSql FreeSql DbFirst 实体生成器 - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/FreeSql-lite.sln b/FreeSql-lite.sln index 989e5166..75f1ac43 100644 --- a/FreeSql-lite.sln +++ b/FreeSql-lite.sln @@ -83,6 +83,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "aspnetcore_transaction", "E EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.AggregateRoot", "Extensions\FreeSql.Extensions.AggregateRoot\FreeSql.Extensions.AggregateRoot.csproj", "{5C78C4CE-3CDC-49C3-AF34-556567B95F2A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "orm_vs", "Examples\orm_vs\orm_vs.csproj", "{9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -453,6 +455,18 @@ Global {5C78C4CE-3CDC-49C3-AF34-556567B95F2A}.Release|x64.Build.0 = Release|Any CPU {5C78C4CE-3CDC-49C3-AF34-556567B95F2A}.Release|x86.ActiveCfg = Release|Any CPU {5C78C4CE-3CDC-49C3-AF34-556567B95F2A}.Release|x86.Build.0 = Release|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Debug|x64.Build.0 = Debug|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Debug|x86.Build.0 = Debug|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Release|Any CPU.Build.0 = Release|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Release|x64.ActiveCfg = Release|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Release|x64.Build.0 = Release|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Release|x86.ActiveCfg = Release|Any CPU + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -484,10 +498,11 @@ Global {19D2E22A-B000-46B6-AFC8-60BF01A51C9A} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {28163C3B-B2E6-432D-AAC3-F5F19374BE31} = {94C8A78D-AA15-47B2-A348-530CD86BFC1B} {5C78C4CE-3CDC-49C3-AF34-556567B95F2A} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} + {9D7EA01A-110A-4A0C-A46B-9A0FBC88DD3D} = {94C8A78D-AA15-47B2-A348-530CD86BFC1B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - RESX_NeutralResourcesLanguage = en-US - RESX_PrefixTranslations = True SolutionGuid = {089687FA-5D21-40AC-BA8A-AA0D1E1H7F98} + RESX_PrefixTranslations = True + RESX_NeutralResourcesLanguage = en-US EndGlobalSection EndGlobal diff --git a/FreeSql.All/FreeSql.All.csproj b/FreeSql.All/FreeSql.All.csproj index e125f524..88e178e1 100644 --- a/FreeSql.All/FreeSql.All.csproj +++ b/FreeSql.All/FreeSql.All.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs b/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs index 068d53d1..bad76689 100644 --- a/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs +++ b/FreeSql.DbContext/DbContext/DbContextScopedFreeSql.cs @@ -70,7 +70,10 @@ namespace FreeSql { var db = _resolveDbContext?.Invoke(); db?.FlushCommand(); - var select = _originalFsql.Select().WithTransaction(_resolveUnitOfWork?.Invoke()?.GetOrBeginTransaction(false)); + var uow = _resolveUnitOfWork?.Invoke(); + var uowIsolationLevel = uow?.IsolationLevel ?? IsolationLevel.Unspecified; + var select = _originalFsql.Select().WithTransaction(uow?.GetOrBeginTransaction(uowIsolationLevel != IsolationLevel.Unspecified)); + (select as Select0Provider)._resolveHookTransaction = () => uow?.GetOrBeginTransaction(); if (db?.Options.EnableGlobalFilter == false) select.DisableGlobalFilter(); return select; } diff --git a/FreeSql.DbContext/DbSet/DbSet.cs b/FreeSql.DbContext/DbSet/DbSet.cs index 59f5a5cc..e3613bed 100644 --- a/FreeSql.DbContext/DbSet/DbSet.cs +++ b/FreeSql.DbContext/DbSet/DbSet.cs @@ -9,6 +9,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Threading; using FreeSql.Internal.CommonProvider; +using System.Data; namespace FreeSql { @@ -34,7 +35,9 @@ namespace FreeSql protected virtual ISelect OrmSelect(object dywhere) { DbContextFlushCommand(); //查询前先提交,否则会出脏读 - var select = _db.OrmOriginal.Select().AsType(_entityType).WithTransaction(_uow?.GetOrBeginTransaction(false)).TrackToList(TrackToList).WhereDynamic(dywhere); + var uowIsolationLevel = _uow?.IsolationLevel ?? IsolationLevel.Unspecified; + var select = _db.OrmOriginal.Select().AsType(_entityType).WithTransaction(_uow?.GetOrBeginTransaction(uowIsolationLevel != IsolationLevel.Unspecified)).TrackToList(TrackToList).WhereDynamic(dywhere); + (select as Select0Provider)._resolveHookTransaction = () => _uow?.GetOrBeginTransaction(); if (_db.Options.EnableGlobalFilter == false) select.DisableGlobalFilter(); return select; } diff --git a/FreeSql.DbContext/FreeSql.DbContext.csproj b/FreeSql.DbContext/FreeSql.DbContext.csproj index c5d9402a..80db1c24 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.csproj +++ b/FreeSql.DbContext/FreeSql.DbContext.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 26522f10..594fbad3 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -733,15 +733,6 @@ - - - 根据Assembly扫描所有继承IEntityTypeConfiguration<T>的配置类 - - - - - - 创建普通数据上下文档对象 @@ -800,14 +791,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 70ea8d90..03b84467 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertTest.cs index e6c259e3..4e10ac87 100644 --- a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertTest.cs +++ b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/Curd/MySqlInsertTest.cs @@ -1,4 +1,4 @@ -using FreeSql.DataAnnotations; +using FreeSql.DataAnnotations; using System; using System.Collections.Generic; using System.Linq; @@ -181,6 +181,32 @@ namespace FreeSql.Tests.MySqlConnector Assert.Equal(TestEnumInserTbType.sum211, g.mysql.Select().Where(a => a.id == id).First()?.type); } + + [Table(Name = "tb_topic_insertbulk")] + class TopicBulk + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + [Fact] + public void ExecuteMySqlBulkCopy() + { + g.mysql.Delete().Where(a => true).ExecuteAffrows(); + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new TopicBulk { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now }); + + g.mysql.Insert(items).ExecuteMySqlBulkCopy(); + + items = g.mysql.Select().OrderByDescending(a => a.Id).Limit(1000).ToList(); + g.mysql.Update().SetSource(items).ExecuteMySqlBulkCopy(); + g.mysql.Update().SetSource(items, a => new { a.Id, a.Clicks }).ExecuteMySqlBulkCopy(); + g.mysql.Update().SetSource(items).UpdateColumns(a => new { a.Title }).ExecuteMySqlBulkCopy(); + g.mysql.Update().SetSource(items, a => new { a.Id, a.Clicks }).UpdateColumns(a => new { a.Title }).ExecuteMySqlBulkCopy(); + } + [Fact] public void AsTable() { diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/MapType/DateTimeOffSetTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/MapType/DateTimeOffSetTest.cs index 91fb979a..8131e69c 100644 --- a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/MapType/DateTimeOffSetTest.cs +++ b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/MySqlConnector/MapType/DateTimeOffSetTest.cs @@ -1,4 +1,5 @@ -using FreeSql.DataAnnotations; +using FreeSql.DataAnnotations; +using MySqlConnector; using System; using System.Numerics; using Xunit; @@ -19,11 +20,17 @@ namespace FreeSql.Tests.MySqlConnectorMapType [Fact] public void DateTimeToDateTimeOffSet() { + + //MySqlDateTime dt1 = new MySqlDateTime(DateTime.Now); + //System.Convert.ChangeType(dt1, typeof(DateTimeOffset)); // System.Exception : Specified cast is not valid. + + //insert var orm = g.mysql; + orm.Delete().Where(a => true).ExecuteAffrows(); var item = new DateTimeOffSetTestMap { dtos_to_dt = DateTimeOffset.Now, dtosnullable_to_dt = DateTimeOffset.Now }; Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); - var find = orm.Select().Where(a => a.id == item.id).First(); + var find = orm.Select().Where(a => a.id == item.id).First(); // System.Exception : Specified cast is not valid. Assert.NotNull(find); Assert.Equal(item.id, find.id); Assert.Equal(item.dtos_to_dt.ToString("g"), find.dtos_to_dt.ToString("g")); diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/g.cs b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/g.cs index cb2c3de6..e18b2c02 100644 --- a/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/g.cs +++ b/FreeSql.Tests/FreeSql.Tests.Provider.MySqlConnector/g.cs @@ -8,7 +8,7 @@ public class g { static Lazy mysqlLazy = new Lazy(() => new FreeSql.FreeSqlBuilder() - .UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;Max pool size=10;AllowLoadLocalInfile=true") + .UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;Max pool size=10;AllowLoadLocalInfile=true;AllowZeroDateTime=True ") //.UseConnectionFactory(FreeSql.DataType.MySql, () => new MySql.Data.MySqlClient.MySqlConnection("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;")) //.UseConnectionString(FreeSql.DataType.MySql, "Data Source=192.168.164.10;Port=33061;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;Max pool size=10") .UseAutoSyncStructure(true) diff --git a/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs new file mode 100644 index 00000000..5b21741d --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Linq; +using System.Collections; +using System.Diagnostics; +using System.ComponentModel.DataAnnotations; +using FreeSql.DataAnnotations; +using Xunit; + +namespace FreeSql.Tests.ClickHouse +{ + public class ClickHouseTest2 + { + private static IFreeSql fsql = new FreeSqlBuilder().UseConnectionString(DataType.ClickHouse, + "Host=127.0.0.1;Port=8123;Database=test;Compress=True;Min Pool Size=1") + .UseMonitorCommand(cmd => Console.WriteLine($"线程:{cmd.CommandText}\r\n")) + .UseNoneCommandParameter(true) + .Build(); + [Fact] + public void CodeFirst() + { + fsql.CodeFirst.SyncStructure(typeof(CollectDataEntityUpdate01)); + } + } +} \ No newline at end of file diff --git a/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity2.cs b/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity2.cs new file mode 100644 index 00000000..112e3620 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity2.cs @@ -0,0 +1,237 @@ +using FreeSql.DataAnnotations; +using Newtonsoft.Json; +using System; +using System.ComponentModel; + +namespace FreeSql.Tests.ClickHouse +{ + /// + /// 实时数据 + /// + [Index("idx_{tablename}_01", nameof(Guid), true)] + [Index("idx_{tablename}_02", nameof(TenantId), true)] + //复合索引 + [Index("idx_{tablename}_03", $"{nameof(CreatedUserId)},{nameof(Version)}", false)] + [Table(OldName = "CollectDataEntityUpdate")] + public partial class CollectDataEntityUpdate01 + { + /// + /// Guid + /// + [Column(StringLength = 50)] + public string Guid + { + get; set; + } + + /// + /// 租户Id + /// + [Description("租户Id")] + [Column(CanUpdate = false)] + public virtual long? TenantId + { + get; set; + } + + /// + /// 版本 + /// + [Description("版本")] + [Column(IsVersion = false)] + public long Version + { + get; set; + } + + /// + /// 是否删除 + /// + [Description("是否删除")] + [Column()] + public bool IsDeleted { get; set; } = false; + + /// + /// 创建者Id + /// + [Description("创建者Id")] + [Column(CanUpdate = false)] + public long? CreatedUserId + { + get; set; + } + + /// + /// 创建者 + /// + [Description("创建者")] + [Column(CanUpdate = false, StringLength = 50, OldName = "CreatedUserNameUpdate")] + public string CreatedUserNameUpdate01 + { + get; set; + } + + /// + /// 创建时间 + /// + [Description("创建时间")] + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime? CreatedTime + { + get; set; + } + + /// + /// 修改者Id + /// + [Description("修改者Id")] + [Column(CanInsert = false)] + public long? ModifiedUserId + { + get; set; + } + + /// + /// 修改者 + /// + [Description("修改者")] + [Column(CanInsert = false, StringLength = 50)] + public string ModifiedUserName + { + get; set; + } + + /// + /// 修改时间 + /// + [Description("修改时间")] + [Column(CanInsert = false, ServerTime = DateTimeKind.Local)] + public DateTime? ModifiedTime + { + get; set; + } + /// + /// 数据标识 + /// + [Description("数据标识")] + [Column(CanInsert = false, StringLength = 2)] + public string DataFlag + { + get; set; + } + /// + /// 主键Id + /// + [Description("主键Id")] + [Column(Position = 1, IsPrimary = true)] + public long Id + { + get; set; + } + /// + /// 设备编号 + /// + [Column(StringLength = 50)] + public string EquipmentCode + { + get; set; + } + + /// + /// 数据编号,如为空使用默认数据 + /// + [Column(StringLength = 50)] + public string PropertyCode + { + get; set; + } + /// + /// 数据名称,如为空使用默认数据 + /// + [Column(StringLength = 50)] + public string PropertyName + { + get; set; + } + + /// + /// 数值或状态是否变更 + /// + public bool IsValueOrStateChanged + { + get; set; + } + + /// + /// 采集数值 + /// + [Column(StringLength = 18)] + public decimal? NumericValue + { + get; set; + } + + /// + /// 备注 + /// + [Column(StringLength = 200)] + public string Remark + { + get; set; + } + + /// + /// 服务标记 + /// + [Column(StringLength = 20)] + public string ServiceFlag + { + get; set; + } + + /// + /// 状态 + /// + [Column(StringLength = 50)] + public string StrState + { + get; set; + } + + /// + /// 文本数值 + /// + [Column(StringLength = 50)] + public string StrValue + { + get; set; + } + + /// + /// 单位 + /// + [Column(StringLength = 10)] + public string UnitStr + { + get; set; + } + + /// + /// 采集时间 + /// + public DateTime CollectTime + { + get; set; + } + + + public string FieldKey + { + get + { + return EquipmentCode + "_" + PropertyCode; + } + } + } + +} \ No newline at end of file diff --git a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml index 46e1feb0..03c7cd98 100644 --- a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml +++ b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml @@ -4,6 +4,126 @@ FreeSql.Tests + + + 实时数据 + + + + + Guid + + + + + 租户Id + + + + + 版本 + + + + + 是否删除 + + + + + 创建者Id + + + + + 创建者 + + + + + 创建时间 + + + + + 修改者Id + + + + + 修改者 + + + + + 修改时间 + + + + + 数据标识 + + + + + 主键Id + + + + + 设备编号 + + + + + 数据编号,如为空使用默认数据 + + + + + 数据名称,如为空使用默认数据 + + + + + 数值或状态是否变更 + + + + + 采集数值 + + + + + 备注 + + + + + 服务标记 + + + + + 状态 + + + + + 文本数值 + + + + + 单位 + + + + + 采集时间 + + 编号 diff --git a/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs index 7299ceec..7ded92e8 100644 --- a/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlInsertOrUpdateTest.cs @@ -15,14 +15,13 @@ namespace FreeSql.Tests.MySql public void InsertOrUpdate_OnePrimary() { fsql.Delete().Where("1=1").ExecuteAffrows(); - var iou = fsql.InsertOrUpdate().SetSource(fsql.Select().ToSql(a => new { id = a.id + 1, name = "xxx" }, FieldAliasOptions.AsProperty)); + var iou = fsql.InsertOrUpdate().SetSource(fsql.Select().ToSql(a => new { id = a.id + 1, name = "'xxx'" }, FieldAliasOptions.AsProperty)); var sql = iou.ToSql(); Assert.Equal(@"INSERT INTO `tbiou02`(`id`, `name`) -SELECT (a.`id` + 1) `id`, xxx `name` +SELECT (a.`id` + 1) `id`, 'xxx' `name` FROM `tbiou022` a ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)", sql); - Assert.Equal(0, iou.ExecuteAffrows()); iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); sql = iou.ToSql(); diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertTest.cs index 6e05d833..6704714b 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/PostgreSQLInsertTest.cs @@ -1,4 +1,4 @@ -using FreeSql.DataAnnotations; +using FreeSql.DataAnnotations; using System; using System.Collections.Generic; using System.Linq; @@ -11,7 +11,7 @@ namespace FreeSql.Tests.PostgreSQL IInsert insert => g.pgsql.Insert(); - [Table(Name = "tb_topic_insert")] + [Table(Name = "tb_topic_insert2")] class Topic { [Column(IsIdentity = true, IsPrimary = true)] @@ -137,6 +137,35 @@ namespace FreeSql.Tests.PostgreSQL insert.AppendData(items.First()).ExecuteInserted(); } + + [Table(Name = "tb_topic_insert_pgcopy")] + class TopicPgCopy + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + + [Fact] + public void ExecutePgCopy() + { + var maxId = g.pgsql.Select().Max(a => a.Id); + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new TopicPgCopy { Id = maxId + a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now }); + + g.pgsql.Insert(items).InsertIdentity().ExecutePgCopy(); + + items = g.pgsql.Select().OrderByDescending(a => a.Id).Limit(1000).ToList(); + var sql = g.pgsql.Insert(items).InsertIdentity().NoneParameter().ToSql(); + g.pgsql.Update().SetSource(items).ExecutePgCopy(); + g.pgsql.Update().SetSource(items, a => new { a.Id, a.Clicks }).ExecutePgCopy(); + g.pgsql.Update().SetSource(items).UpdateColumns(a => new { a.Title }).ExecutePgCopy(); + g.pgsql.Update().SetSource(items, a => new { a.Id, a.Clicks }).UpdateColumns(a => new { a.Title }).ExecutePgCopy(); + } + [Fact] public void AsTable() { diff --git a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs index c0cc53f3..a960da06 100644 --- a/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/SqlServer/Curd/SqlServerInsertTest.cs @@ -227,12 +227,19 @@ namespace FreeSql.Tests.SqlServer [Fact] public void ExecuteSqlBulkCopy() { + var maxId = g.pgsql.Select().Max(a => a.Id); var items = new List(); - for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now }); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = maxId + a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now }); insert.AppendData(items).InsertIdentity().ExecuteSqlBulkCopy(); //insert.AppendData(items).IgnoreColumns(a => new { a.CreateTime, a.Clicks }).ExecuteSqlBulkCopy(); // System.NotSupportedException:“DataSet does not support System.Nullable<>.” + + items = g.sqlserver.Select().OrderByDescending(a => a.Id).Limit(1000).ToList(); + g.sqlserver.Update().SetSource(items).ExecuteSqlBulkCopy(); + g.sqlserver.Update().SetSource(items, a => new { a.Id, a.TypeGuid }).ExecuteSqlBulkCopy(); + g.sqlserver.Update().SetSource(items).UpdateColumns(a => new { a.Title }).ExecuteSqlBulkCopy(); + g.sqlserver.Update().SetSource(items, a => new { a.Id, a.TypeGuid }).UpdateColumns(a => new { a.Title }).ExecuteSqlBulkCopy(); } [Fact] diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index cbb5208c..7ce2c34c 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -896,7 +896,7 @@ SELECT "); static void LocalReplaceDictDBNullValue(Dictionary dict) { if (dict == null) return; - var keys = dict.Keys; + var keys = dict.Keys.ToArray(); foreach (var key in keys) { var val = dict[key]; diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index a1d89c2a..897ae9a1 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 302c8ee7..21395735 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1336,7 +1336,7 @@ 数据库类型 数据库连接串 - 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场 + 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场例如:typeof(FreeSql.SqlServer.SqlServerProvider<>) @@ -1352,7 +1352,7 @@ 数据库类型 数据库连接对象创建器 - 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场 + 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场例如:typeof(FreeSql.SqlServer.SqlServerProvider<>) @@ -1413,6 +1413,15 @@ + + + SQL名称是否使用 [] `` "" + true: SELECT .. FROM [table] + false: SELECT .. FROM table + + + + 指定映射优先级 @@ -1439,29 +1448,6 @@ - - - 转小写同步结构 - - true:转小写, false:不转 - - - - - 转大写同步结构 - - true:转大写, false:不转 - - - - - 自动转换实体属性名称 Entity Property -> Db Filed - - *不会覆盖 [Column] 特性设置的Name - - - - 指定事务对象 @@ -4174,46 +4160,6 @@ BigApple -> bigapple - - - 不进行任何处理 - - - - - 将帕斯卡命名字符串转换为下划线分隔字符串 - - BigApple -> Big_Apple - - - - - 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 - - BigApple -> BIG_APPLE - - - - - 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 - - BigApple -> big_apple - - - - - 将字符串转换为大写 - - BigApple -> BIGAPPLE - - - - - 将字符串转换为小写 - - BigApple -> bigapple - - 创建一个过滤器 diff --git a/FreeSql/FreeSqlBuilder.cs b/FreeSql/FreeSqlBuilder.cs index 51e3b3dc..78cfbbcf 100644 --- a/FreeSql/FreeSqlBuilder.cs +++ b/FreeSql/FreeSqlBuilder.cs @@ -18,15 +18,13 @@ namespace FreeSql int[] _slaveWeights; Func _connectionFactory; bool _isAutoSyncStructure = false; - bool _isSyncStructureToLower = false; - bool _isSyncStructureToUpper = false; bool _isConfigEntityFromDbFirst = false; bool _isNoneCommandParameter = false; bool _isGenerateCommandParameterWithLambda = false; bool _isLazyLoading = false; bool _isExitAutoDisposePool = true; + bool _isQuoteSqlName = true; MappingPriorityType[] _mappingPriorityTypes; - StringConvertType _entityPropertyConvertType = StringConvertType.None; NameConvertType _nameConvertType = NameConvertType.None; Action _aopCommandExecuting = null; Action _aopCommandExecuted = null; @@ -37,7 +35,7 @@ namespace FreeSql /// /// 数据库类型 /// 数据库连接串 - /// 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场 + /// 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场例如:typeof(FreeSql.SqlServer.SqlServerProvider<>) /// public FreeSqlBuilder UseConnectionString(DataType dataType, string connectionString, Type providerType = null) { @@ -69,7 +67,7 @@ namespace FreeSql /// /// 数据库类型 /// 数据库连接对象创建器 - /// 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场 + /// 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场例如:typeof(FreeSql.SqlServer.SqlServerProvider<>) /// public FreeSqlBuilder UseConnectionFactory(DataType dataType, Func connectionFactory, Type providerType = null) { @@ -161,6 +159,18 @@ namespace FreeSql _nameConvertType = convertType; return this; } + /// + /// SQL名称是否使用 [] `` "" + /// true: SELECT .. FROM [table] + /// false: SELECT .. FROM table + /// + /// + /// + public FreeSqlBuilder UseQuoteSqlName(bool value) + { + _isQuoteSqlName = value; + return this; + } /// /// 指定映射优先级 @@ -337,8 +347,6 @@ namespace FreeSql { ret.CodeFirst.IsAutoSyncStructure = _isAutoSyncStructure; - ret.CodeFirst.IsSyncStructureToLower = _isSyncStructureToLower; - ret.CodeFirst.IsSyncStructureToUpper = _isSyncStructureToUpper; ret.CodeFirst.IsConfigEntityFromDbFirst = _isConfigEntityFromDbFirst; ret.CodeFirst.IsNoneCommandParameter = _isNoneCommandParameter; ret.CodeFirst.IsGenerateCommandParameterWithLambda = _isGenerateCommandParameterWithLambda; @@ -352,7 +360,6 @@ namespace FreeSql if (_aopCommandExecuted != null) ret.Aop.CommandAfter += new EventHandler((s, e) => _aopCommandExecuted?.Invoke(e.Command, e.Log)); - this.EntityPropertyNameConvert(ret); //添加实体属性名全局AOP转换处理 if (_nameConvertType != NameConvertType.None) { @@ -517,6 +524,8 @@ namespace FreeSql if (_slaveWeights != null) for (var x = 0; x < _slaveWeights.Length; x++) ret.Ado.SlavePools[x].Policy.Weight = _slaveWeights[x]; + + (ret.Select() as Select0Provider)._commonUtils.IsQuoteSqlName = _isQuoteSqlName; } return ret; diff --git a/FreeSql/FreeSqlBuilder_Obsolete.cs b/FreeSql/FreeSqlBuilder_Obsolete.cs deleted file mode 100644 index b4021628..00000000 --- a/FreeSql/FreeSqlBuilder_Obsolete.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Data.Common; -using System.Linq; -using System.Reflection; -using FreeSql.DataAnnotations; -using FreeSql.Internal; - -namespace FreeSql -{ - partial class FreeSqlBuilder - { - - /// - /// 转小写同步结构 - /// - /// true:转小写, false:不转 - /// - [Obsolete("请使用 UseNameConvert(NameConvertType.ToLower),或者 fsql.CodeFirst.IsSyncStructureToLower = value")] - public FreeSqlBuilder UseSyncStructureToLower(bool value) - { - _isSyncStructureToLower = value; - return this; - } - /// - /// 转大写同步结构 - /// - /// true:转大写, false:不转 - /// - [Obsolete("请使用 UseNameConvert(NameConvertType.ToUpper),或者 fsql.CodeFirst.IsSyncStructureToUpper = value")] - public FreeSqlBuilder UseSyncStructureToUpper(bool value) - { - _isSyncStructureToUpper = value; - return this; - } - - /// - /// 自动转换实体属性名称 Entity Property -> Db Filed - /// - /// *不会覆盖 [Column] 特性设置的Name - /// - /// - /// - [Obsolete("请使用 UseNameConvert 功能")] - public FreeSqlBuilder UseEntityPropertyNameConvert(StringConvertType convertType) - { - _entityPropertyConvertType = convertType; - return this; - } - - void EntityPropertyNameConvert(IFreeSql fsql) - { - //添加实体属性名全局AOP转换处理 - if (_entityPropertyConvertType != StringConvertType.None) - { - string PascalCaseToUnderScore(string str) => string.IsNullOrWhiteSpace(str) ? str : string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())); - - switch (_entityPropertyConvertType) - { - case StringConvertType.Lower: - fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = e.ModifyResult.Name?.ToLower(); - break; - case StringConvertType.Upper: - fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = e.ModifyResult.Name?.ToUpper(); - break; - case StringConvertType.PascalCaseToUnderscore: - fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.ModifyResult.Name); - break; - case StringConvertType.PascalCaseToUnderscoreWithLower: - fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.ModifyResult.Name)?.ToLower(); - break; - case StringConvertType.PascalCaseToUnderscoreWithUpper: - fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = PascalCaseToUnderScore(e.ModifyResult.Name)?.ToUpper(); - break; - default: - break; - } - } - } - } -} diff --git a/FreeSql/FreeUtil.cs b/FreeSql/FreeUtil.cs index 37e928c9..400f9aa0 100644 --- a/FreeSql/FreeUtil.cs +++ b/FreeSql/FreeUtil.cs @@ -12,7 +12,7 @@ public static class FreeUtil { private static DateTime dt1970 = new DateTime(1970, 1, 1); - private static ThreadLocal rnd = new ThreadLocal(() => new Random()); + internal static ThreadLocal rnd = new ThreadLocal(() => new Random()); private static readonly int __staticMachine = ((0x00ffffff & Environment.MachineName.GetHashCode()) + #if NETSTANDARD1_5 || NETSTANDARD1_6 1 diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 671a7e7c..85867997 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -277,15 +277,23 @@ namespace FreeSql.Internal return false; } if (parent.CsType == null) parent.CsType = exp.Type; - var pdbfield = parent.DbField = ExpressionLambdaToSql(exp, getTSC()); - if (parent.MapType == null || _tables?.Any(a => a.Table?.IsRereadSql == true) == true) + try { - var findcol = SearchColumnByField(_tables, null, parent.DbField); - if (parent.MapType == null) parent.MapType = findcol?.Attribute.MapType ?? exp.Type; - if (findcol != null) pdbfield = _common.RereadColumn(findcol, pdbfield); + var pdbfield = parent.DbField = ExpressionLambdaToSql(exp, getTSC()); + if (parent.MapType == null || _tables?.Any(a => a.Table?.IsRereadSql == true) == true) + { + var findcol = SearchColumnByField(_tables, null, parent.DbField); + if (parent.MapType == null) parent.MapType = findcol?.Attribute.MapType ?? exp.Type; + if (findcol != null) pdbfield = _common.RereadColumn(findcol, pdbfield); + } + field.Append(", ").Append(pdbfield); + LocalSetFieldAlias(ref index, _tables != null || + SelectGroupingProvider._ParseExpOnlyDbField.Value != pdbfield); + } + finally + { + SelectGroupingProvider._ParseExpOnlyDbField.Value = null; } - field.Append(", ").Append(pdbfield); - LocalSetFieldAlias(ref index, true); return false; } return false; @@ -923,7 +931,16 @@ namespace FreeSql.Internal return $"not({ExpressionLambdaToSql(notExp, tsc)})"; case ExpressionType.Quote: return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc); case ExpressionType.Lambda: return ExpressionLambdaToSql((exp as LambdaExpression)?.Body, tsc); - case ExpressionType.Invoke: return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); + //case ExpressionType.Invoke: return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); + case ExpressionType.Invoke: + var invokeExp = exp as InvocationExpression; + var invokeReplaceVistor = new FreeSql.Internal.CommonExpression.ReplaceVisitor(); + var invokeReplaceExp = invokeExp.Expression; + var invokeLambdaExp = invokeReplaceExp as LambdaExpression; + var len = Math.Min(invokeExp.Arguments.Count, invokeLambdaExp.Parameters.Count); + for (var a = 0; a < len; a++) + invokeReplaceExp = invokeReplaceVistor.Modify(invokeReplaceExp, invokeLambdaExp.Parameters[a], invokeExp.Arguments[a]); + return ExpressionLambdaToSql(invokeReplaceExp, tsc); case ExpressionType.TypeAs: case ExpressionType.Convert: case ExpressionType.ConvertChecked: diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs index ae30c2fe..b6ed248c 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs @@ -33,7 +33,6 @@ namespace FreeSql.Internal.CommonProvider public CommonUtils _util { get; set; } protected int slaveUnavailables = 0; private object slaveLock = new object(); - private Random slaveRandom = new Random(); protected Func ResolveTransaction; public AdoProvider(DataType dataType, string connectionString, string[] slaveConnectionStrings) @@ -52,7 +51,7 @@ namespace FreeSql.Internal.CommonProvider { TimeSpan ts = DateTime.Now.Subtract(dt); if (ex == null && ts.TotalMilliseconds > 100) - Trace.WriteLine(logtxt.Insert(0, $"{pool?.Policy.Name}(执行SQL)语句耗时过长{ts.TotalMilliseconds}ms\r\n{cmd.CommandText}\r\n").ToString()); + logtxt.Insert(0, $"{pool?.Policy.Name}(执行SQL)语句耗时过长{ts.TotalMilliseconds}ms\r\n{cmd.CommandText}\r\n").ToString(); else logtxt.Insert(0, $"{pool?.Policy.Name}(执行SQL)耗时{ts.TotalMilliseconds}ms\r\n{cmd.CommandText}\r\n").ToString(); } @@ -69,7 +68,7 @@ namespace FreeSql.Internal.CommonProvider log.Append(parm.ParameterName.PadRight(20, ' ')).Append(" = ").Append((parm.Value ?? DBNull.Value) == DBNull.Value ? "NULL" : parm.Value).Append("\r\n"); log.Append(ex.Message); - Trace.WriteLine(log.ToString()); + //Trace.WriteLine(log.ToString()); if (cmd.Transaction != null) { @@ -152,6 +151,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -195,6 +195,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -212,6 +213,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -263,6 +265,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -280,6 +283,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -297,6 +301,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -354,6 +359,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -371,6 +377,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -388,6 +395,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -405,6 +413,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -468,6 +477,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -485,6 +495,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -502,6 +513,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -519,6 +531,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -536,6 +549,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -584,7 +598,7 @@ namespace FreeSql.Internal.CommonProvider if (availables.Count == 1) pool = availables[0]; else { - var rnd = slaveRandom.Next(availables.Sum(a => a.Policy.Weight)); + var rnd = FreeUtil.rnd.Value.Next(availables.Sum(a => a.Policy.Weight)); for(var a = 0; a < availables.Count; a++) { rnd -= availables[a].Policy.Weight; @@ -601,6 +615,14 @@ namespace FreeSql.Internal.CommonProvider Object conn = null; var pc = PrepareCommand(connection, transaction, cmdType, cmdText, cmdTimeout, cmdParms, logtxt); + if (string.IsNullOrEmpty(pc.cmd.CommandText)) //被拦截 CommandBefore + { + LoggerException(pool, pc, null, dt, logtxt); + pc.cmd.Parameters.Clear(); + if (DataType == DataType.Sqlite) pc.cmd.Dispose(); + return; + } + if (IsTracePerformance) { logtxt.Append("PrepareCommand: ").Append(DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds).Append("ms Total: ").Append(DateTime.Now.Subtract(dt).TotalMilliseconds).Append("ms\r\n"); @@ -785,8 +807,11 @@ namespace FreeSql.Internal.CommonProvider Exception ex = null; try { - if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = this.MasterPool.Get()).Value; - val = pc.cmd.ExecuteNonQuery(); + if (string.IsNullOrEmpty(pc.cmd.CommandText) == false) //是否被拦截 CommandBefore + { + if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = this.MasterPool.Get()).Value; + val = pc.cmd.ExecuteNonQuery(); + } } catch (Exception ex2) { @@ -821,8 +846,11 @@ namespace FreeSql.Internal.CommonProvider Exception ex = null; try { - if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = this.MasterPool.Get()).Value; - val = pc.cmd.ExecuteScalar(); + if (string.IsNullOrEmpty(pc.cmd.CommandText) == false) //是否被拦截 CommandBefore + { + if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = this.MasterPool.Get()).Value; + val = pc.cmd.ExecuteScalar(); + } } catch (Exception ex2) { diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs index 23a12c2f..2a476453 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs @@ -67,6 +67,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -111,6 +112,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -128,6 +130,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -180,6 +183,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -197,6 +201,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -214,6 +219,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -272,6 +278,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -289,6 +296,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -306,6 +314,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -323,6 +332,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -387,6 +397,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -404,6 +415,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -421,6 +433,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -438,6 +451,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -455,6 +469,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < fetch.Object.FieldCount; a++) { var name = fetch.Object.GetName(a); + if (DataType == DataType.ClickHouse && name.Contains(".")) name = name.Substring(name.IndexOf('.') + 1); if (dic.ContainsKey(name)) continue; sbflag.Append(name).Append(":").Append(a).Append(","); dic.Add(name, a); @@ -501,13 +516,34 @@ namespace FreeSql.Internal.CommonProvider if (availables.Any()) { isSlave = true; - pool = availables.Count == 1 ? this.SlavePools[0] : availables[slaveRandom.Next(availables.Count)]; + if (availables.Count == 1) pool = availables[0]; + else + { + var rnd = FreeUtil.rnd.Value.Next(availables.Sum(a => a.Policy.Weight)); + for (var a = 0; a < availables.Count; a++) + { + rnd -= availables[a].Policy.Weight; + if (rnd < 0) + { + pool = availables[a]; + break; + } + } + } } } } Object conn = null; var pc = await PrepareCommandAsync(connection, transaction, cmdType, cmdText, cmdTimeout, cmdParms, logtxt, cancellationToken); + if (string.IsNullOrEmpty(pc.cmd.CommandText)) //被拦截 CommandBefore + { + LoggerException(pool, pc, null, dt, logtxt); + pc.cmd.Parameters.Clear(); + if (DataType == DataType.Sqlite) pc.cmd.Dispose(); + return; + } + if (IsTracePerformance) { logtxt.Append("PrepareCommand: ").Append(DateTime.Now.Subtract(logtxt_dt).TotalMilliseconds).Append("ms Total: ").Append(DateTime.Now.Subtract(dt).TotalMilliseconds).Append("ms\r\n"); @@ -690,8 +726,11 @@ namespace FreeSql.Internal.CommonProvider Exception ex = null; try { - if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = await this.MasterPool.GetAsync()).Value; - val = await pc.cmd.ExecuteNonQueryAsync(cancellationToken); + if (string.IsNullOrEmpty(pc.cmd.CommandText) == false) //是否被拦截 CommandBefore + { + if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = await this.MasterPool.GetAsync()).Value; + val = await pc.cmd.ExecuteNonQueryAsync(cancellationToken); + } } catch (Exception ex2) { @@ -726,8 +765,11 @@ namespace FreeSql.Internal.CommonProvider Exception ex = null; try { - if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = await this.MasterPool.GetAsync()).Value; - val = await pc.cmd.ExecuteScalarAsync(cancellationToken); + if (string.IsNullOrEmpty(pc.cmd.CommandText) == false) //是否被拦截 CommandBefore + { + if (pc.cmd.Connection == null) pc.cmd.Connection = (conn = await this.MasterPool.GetAsync()).Value; + val = await pc.cmd.ExecuteScalarAsync(cancellationToken); + } } catch (Exception ex2) { diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderTransaction.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderTransaction.cs index f0e49e74..e87abc32 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderTransaction.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderTransaction.cs @@ -53,7 +53,7 @@ namespace FreeSql.Internal.CommonProvider } catch (Exception ex) { - Trace.WriteLine($"数据库出错(开启事务){ex.Message} \r\n{ex.StackTrace}"); + //Trace.WriteLine($"数据库出错(开启事务){ex.Message} \r\n{ex.StackTrace}"); MasterPool.Return(conn); var after = new Aop.TraceAfterEventArgs(before, "", ex); _util?._orm?.Aop.TraceAfterHandler?.Invoke(this, after); @@ -83,7 +83,7 @@ namespace FreeSql.Internal.CommonProvider { if (tran.Transaction.Connection != null) //用户自行 Commit、Rollback { - Trace.WriteLine($"线程{tran.Connection.LastGetThreadId}事务{remark}"); + //Trace.WriteLine($"线程{tran.Connection.LastGetThreadId}事务{remark}"); if (isCommit) tran.Transaction.Commit(); else tran.Transaction.Rollback(); } @@ -91,7 +91,7 @@ namespace FreeSql.Internal.CommonProvider catch (Exception ex2) { ex = ex2; - Trace.WriteLine($"数据库出错({remark}事务):{ex.Message} {ex.StackTrace}"); + //Trace.WriteLine($"数据库出错({remark}事务):{ex.Message} {ex.StackTrace}"); } finally { diff --git a/FreeSql/Internal/CommonProvider/DeleteProvider.cs b/FreeSql/Internal/CommonProvider/DeleteProvider.cs index 38cef112..58bca8e8 100644 --- a/FreeSql/Internal/CommonProvider/DeleteProvider.cs +++ b/FreeSql/Internal/CommonProvider/DeleteProvider.cs @@ -25,6 +25,7 @@ namespace FreeSql.Internal.CommonProvider public DbConnection _connection; public int _commandTimeout = 0; public Action _interceptSql; + public bool _isAutoSyncStructure; } public abstract partial class DeleteProvider : DeleteProvider, IDelete @@ -35,8 +36,9 @@ namespace FreeSql.Internal.CommonProvider _commonUtils = commonUtils; _commonExpression = commonExpression; _table = _commonUtils.GetTableByEntity(typeof(T1)); + _isAutoSyncStructure = _orm.CodeFirst.IsAutoSyncStructure; this.Where(_commonUtils.WhereObject(_table, "", dywhere)); - if (_orm.CodeFirst.IsAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); + if (_isAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); _whereGlobalFilter = _orm.GlobalFilter.GetFilters(); } @@ -51,7 +53,7 @@ namespace FreeSql.Internal.CommonProvider public IDelete WithTransaction(DbTransaction transaction) { _transaction = transaction; - _connection = _transaction?.Connection; + if (transaction != null) _connection = transaction.Connection; return this; } public IDelete WithConnection(DbConnection connection) @@ -145,7 +147,7 @@ namespace FreeSql.Internal.CommonProvider if (string.IsNullOrEmpty(newname)) return _table.DbName; if (_orm.CodeFirst.IsSyncStructureToLower) newname = newname.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) newname = newname.ToUpper(); - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table.Type, newname); + if (_isAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table.Type, newname); return newname; } public IDelete AsTable(Func tableRule) @@ -164,7 +166,7 @@ namespace FreeSql.Internal.CommonProvider if (entityType == _table.Type) return this; var newtb = _commonUtils.GetTableByEntity(entityType); _table = newtb ?? throw new Exception(CoreStrings.Type_AsType_Parameter_Error("IDelete")); - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); + if (_isAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); return this; } diff --git a/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs b/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs index 082358d4..557fcdf2 100644 --- a/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/InsertOrUpdateProvider.cs @@ -61,7 +61,7 @@ namespace FreeSql.Internal.CommonProvider public IInsertOrUpdate WithTransaction(DbTransaction transaction) { _transaction = transaction; - _connection = _transaction?.Connection; + if (transaction != null) _connection = transaction.Connection; return this; } public IInsertOrUpdate WithConnection(DbConnection connection) @@ -290,6 +290,7 @@ namespace FreeSql.Internal.CommonProvider public abstract string ToSql(); public int ExecuteAffrows() { + if (_sourceSql != null) return this.RawExecuteAffrows(); var affrows = 0; var ss = SplitSourceByIdentityValueIsNull(_source); try @@ -408,6 +409,7 @@ namespace FreeSql.Internal.CommonProvider } async public Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) { + if (_sourceSql != null) return this.RawExecuteAffrows(); var affrows = 0; var ss = SplitSourceByIdentityValueIsNull(_source); try diff --git a/FreeSql/Internal/CommonProvider/InsertProvider.cs b/FreeSql/Internal/CommonProvider/InsertProvider.cs index d7ea18b3..7bd2e76e 100644 --- a/FreeSql/Internal/CommonProvider/InsertProvider.cs +++ b/FreeSql/Internal/CommonProvider/InsertProvider.cs @@ -31,6 +31,7 @@ namespace FreeSql.Internal.CommonProvider public DbTransaction _transaction; public DbConnection _connection; public int _commandTimeout = 0; + public bool _isAutoSyncStructure; } public abstract partial class InsertProvider : InsertProvider, IInsert where T1 : class @@ -46,7 +47,8 @@ namespace FreeSql.Internal.CommonProvider _commonExpression = commonExpression; _table = _commonUtils.GetTableByEntity(typeof(T1)); _noneParameter = _orm.CodeFirst.IsNoneCommandParameter; - if (_orm.CodeFirst.IsAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); + _isAutoSyncStructure = _orm.CodeFirst.IsAutoSyncStructure; + if (_isAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); IgnoreCanInsert(); _sourceOld = _source; } @@ -78,7 +80,7 @@ namespace FreeSql.Internal.CommonProvider public IInsert WithTransaction(DbTransaction transaction) { _transaction = transaction; - _connection = _transaction?.Connection; + if (transaction != null) _connection = transaction.Connection; return this; } public IInsert WithConnection(DbConnection connection) @@ -568,7 +570,7 @@ namespace FreeSql.Internal.CommonProvider if (string.IsNullOrEmpty(newname)) return tbname; if (_orm.CodeFirst.IsSyncStructureToLower) newname = newname.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) newname = newname.ToUpper(); - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table?.Type, newname); + if (_isAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table?.Type, newname); return newname; } public IInsert AsTable(Func tableRule) @@ -588,7 +590,7 @@ namespace FreeSql.Internal.CommonProvider if (entityType == _table.Type) return this; var newtb = _commonUtils.GetTableByEntity(entityType); _table = newtb ?? throw new Exception(CoreStrings.Type_AsType_Parameter_Error("IInsert")); - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); + if (_isAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); IgnoreCanInsert(); return this; } diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index a59076bf..f2ac8e0c 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -47,6 +47,7 @@ namespace FreeSql.Internal.CommonProvider public Func _cancel; public bool _is_AsTreeCte; public BaseDiyMemberExpression _diymemexpWithTempQuery; + public Func _resolveHookTransaction; public bool IsDefaultSqlContent => _distinct == false && _is_AsTreeCte == false && _tables.Count == 1 && _where.Length == 0 && _join.Length == 0 && string.IsNullOrWhiteSpace(_orderby) && string.IsNullOrWhiteSpace(_groupby) && string.IsNullOrWhiteSpace(_tosqlAppendContent) && @@ -557,7 +558,7 @@ namespace FreeSql.Internal.CommonProvider public TSelect WithTransaction(DbTransaction transaction) { _transaction = transaction; - _connection = _transaction?.Connection; + if (transaction != null) _connection = transaction.Connection; return this as TSelect; } public TSelect WithConnection(DbConnection connection) @@ -1187,8 +1188,9 @@ namespace FreeSql.Internal.CommonProvider } public TSelect ForUpdate(bool noawait = false) { - if (_transaction == null && _orm.Ado.TransactionCurrentThread == null) - throw new Exception($"{CoreStrings.Begin_Transaction_Then_ForUpdate}"); + if (_transaction == null && _orm.Ado.TransactionCurrentThread != null) this.WithTransaction(_orm.Ado.TransactionCurrentThread); + if (_transaction == null && _resolveHookTransaction != null) this.WithTransaction(_resolveHookTransaction()); + if (_transaction == null) throw new Exception($"{CoreStrings.Begin_Transaction_Then_ForUpdate}"); switch (_orm.Ado.DataType) { case DataType.MySql: diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs index 7d26c47e..c719221f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs @@ -31,6 +31,7 @@ namespace FreeSql.Internal.CommonProvider _tables = tables; } + public static ThreadLocal _ParseExpOnlyDbField = new ThreadLocal(); public override string ParseExp(Expression[] members) { ParseExpMapResult = null; @@ -52,7 +53,11 @@ namespace FreeSql.Internal.CommonProvider } ParseExpMapResult = read; if (!_addFieldAlias) return read.DbField; - if (_comonExp.EndsWithDbNestedField(read.DbField, read.DbNestedField) == false) return $"{read.DbField}{_comonExp._common.FieldAsAlias(read.DbNestedField)}"; + if (_comonExp.EndsWithDbNestedField(read.DbField, read.DbNestedField) == false) + { + _ParseExpOnlyDbField.Value = read.DbField; + return $"{read.DbField}{_comonExp._common.FieldAsAlias(read.DbNestedField)}"; + } return read.DbField; case "Value": var curtables = _tables; diff --git a/FreeSql/Internal/CommonProvider/UpdateProvider.cs b/FreeSql/Internal/CommonProvider/UpdateProvider.cs index 5448e93c..3ed7697e 100644 --- a/FreeSql/Internal/CommonProvider/UpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/UpdateProvider.cs @@ -1,5 +1,6 @@ using FreeSql.Extensions.EntityUtil; using FreeSql.Internal.Model; +using FreeSql.Internal.ObjectPool; using System; using System.Collections.Generic; using System.Data; @@ -37,6 +38,96 @@ namespace FreeSql.Internal.CommonProvider public int _commandTimeout = 0; public Action _interceptSql; public object _updateVersionValue; + public bool _isAutoSyncStructure; + + + public static int ExecuteBulkUpdate(UpdateProvider update, NativeTuple state, Action> funcBulkCopy) where T1 : class + { + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0; + var fsql = update._orm; + var connection = update._connection; + var transaction = update._transaction; + + Object poolConn = null; + if (connection == null) + { + poolConn = fsql.Ado.MasterPool.Get(); + connection = poolConn.Value; + } + try + { + var droped = false; + fsql.Ado.CommandFluent(state.Item1).WithConnection(connection).WithTransaction(transaction).ExecuteNonQuery(); + try + { + var insert = fsql.Insert(update._source) + .AsType(update._table.Type) + .WithConnection(connection) + .WithTransaction(transaction) + .InsertIdentity() + .InsertColumns(state.Item5) + .AsTable(state.Item4); + (insert as InsertProvider)._isAutoSyncStructure = false; + funcBulkCopy(insert); + var affrows = fsql.Ado.CommandFluent(state.Item2 + ";\r\n" + state.Item3).WithConnection(connection).WithTransaction(transaction).ExecuteNonQuery(); + droped = true; + return affrows; + } + finally + { + if (droped == false) fsql.Ado.CommandFluent(state.Item3).WithConnection(connection).WithTransaction(transaction).ExecuteNonQuery(); + } + } + finally + { + poolConn?.Dispose(); + } + } +#if net40 +#else + async public static Task ExecuteBulkUpdateAsync(UpdateProvider update, NativeTuple state, Func, Task> funcBulkCopy) where T1 : class + { + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0; + var fsql = update._orm; + var connection = update._connection; + var transaction = update._transaction; + + Object poolConn = null; + if (connection == null) + { + poolConn = await fsql.Ado.MasterPool.GetAsync(); + connection = poolConn.Value; + } + try + { + var droped = false; + await fsql.Ado.CommandFluent(state.Item1).WithConnection(connection).WithTransaction(transaction).ExecuteNonQueryAsync(); + try + { + var insert = fsql.Insert(update._source) + .AsType(update._table.Type) + .WithConnection(connection) + .WithTransaction(transaction) + .InsertIdentity() + .InsertColumns(state.Item5) + .AsTable(state.Item4); + (insert as InsertProvider)._isAutoSyncStructure = false; + await funcBulkCopy(insert); + var affrows = await fsql.Ado.CommandFluent(state.Item2 + ";\r\n" + state.Item3).WithConnection(connection).WithTransaction(transaction).ExecuteNonQueryAsync(); + droped = true; + return affrows; + } + finally + { + if (droped == false) await fsql.Ado.CommandFluent(state.Item3).WithConnection(connection).WithTransaction(transaction).ExecuteNonQueryAsync(); + } + } + finally + { + poolConn?.Dispose(); + } + } +#endif } public abstract partial class UpdateProvider : UpdateProvider, IUpdate @@ -54,8 +145,9 @@ namespace FreeSql.Internal.CommonProvider _tempPrimarys = _table?.Primarys ?? new ColumnInfo[0]; _versionColumn = _table?.VersionColumn; _noneParameter = _orm.CodeFirst.IsNoneCommandParameter; + _isAutoSyncStructure = _orm.CodeFirst.IsAutoSyncStructure; this.Where(_commonUtils.WhereObject(_table, "", dywhere)); - if (_orm.CodeFirst.IsAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); + if (_isAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); IgnoreCanUpdate(); _whereGlobalFilter = _orm.GlobalFilter.GetFilters(); _sourceOld = _source; @@ -95,7 +187,7 @@ namespace FreeSql.Internal.CommonProvider public IUpdate WithTransaction(DbTransaction transaction) { _transaction = transaction; - _connection = _transaction?.Connection; + if (transaction != null) _connection = transaction.Connection; return this; } public IUpdate WithConnection(DbConnection connection) @@ -797,7 +889,7 @@ namespace FreeSql.Internal.CommonProvider if (string.IsNullOrEmpty(newname)) return _table.DbName; if (_orm.CodeFirst.IsSyncStructureToLower) newname = newname.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) newname = newname.ToUpper(); - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table.Type, newname); + if (_isAutoSyncStructure) _orm.CodeFirst.SyncStructure(_table.Type, newname); return newname; } public IUpdate AsTable(Func tableRule) @@ -818,7 +910,7 @@ namespace FreeSql.Internal.CommonProvider _table = newtb ?? throw new Exception(CoreStrings.Type_AsType_Parameter_Error("IUpdate")); _tempPrimarys = _table.Primarys; _versionColumn = _ignoreVersion ? null : _table.VersionColumn; - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); + if (_isAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); IgnoreCanUpdate(); return this; } diff --git a/FreeSql/Internal/CommonUtils.cs b/FreeSql/Internal/CommonUtils.cs index 8ad03191..bc848f6b 100644 --- a/FreeSql/Internal/CommonUtils.cs +++ b/FreeSql/Internal/CommonUtils.cs @@ -28,7 +28,14 @@ namespace FreeSql.Internal public abstract DbParameter AppendParamter(List _params, string parameterName, ColumnInfo col, Type type, object value); public abstract DbParameter[] GetDbParamtersByObject(string sql, object obj); public abstract string FormatSql(string sql, params object[] args); - public abstract string QuoteSqlName(params string[] name); + + public bool IsQuoteSqlName = true; + public string QuoteSqlName(params string[] name) { + if (IsQuoteSqlName) return QuoteSqlNameAdapter(name); + if (name == null) return ""; + return string.Join(".", name); + } + public abstract string QuoteSqlNameAdapter(params string[] name); public abstract string TrimQuoteSqlName(string name); public abstract string[] SplitTableName(string name); public static string[] GetSplitTableNames(string name, char leftQuote, char rightQuote, int size) diff --git a/FreeSql/Internal/FreeSqlBuilderTypes.cs b/FreeSql/Internal/FreeSqlBuilderTypes.cs index 4d3a4541..4910e886 100644 --- a/FreeSql/Internal/FreeSqlBuilderTypes.cs +++ b/FreeSql/Internal/FreeSqlBuilderTypes.cs @@ -79,47 +79,4 @@ namespace FreeSql.Internal ToLower } - public enum StringConvertType - { - /// - /// 不进行任何处理 - /// - None = 0, - - /// - /// 将帕斯卡命名字符串转换为下划线分隔字符串 - /// - /// BigApple -> Big_Apple - /// - PascalCaseToUnderscore, - - /// - /// 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 - /// - /// BigApple -> BIG_APPLE - /// - PascalCaseToUnderscoreWithUpper, - - /// - /// 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 - /// - /// BigApple -> big_apple - /// - PascalCaseToUnderscoreWithLower, - - /// - /// 将字符串转换为大写 - /// - /// BigApple -> BIGAPPLE - /// - Upper, - - /// - /// 将字符串转换为小写 - /// - /// BigApple -> bigapple - /// - Lower - } - } \ No newline at end of file diff --git a/FreeSql/Internal/ObjectPool/ObjectPool.cs b/FreeSql/Internal/ObjectPool/ObjectPool.cs index 2f63fdb2..6466eaca 100644 --- a/FreeSql/Internal/ObjectPool/ObjectPool.cs +++ b/FreeSql/Internal/ObjectPool/ObjectPool.cs @@ -250,6 +250,29 @@ namespace FreeSql.Internal.ObjectPool catch { } } + public void AutoFree() + { + if (running == false) return; + if (UnavailableException != null) return; + + var list = new List>(); + while (_freeObjects.TryPop(out var obj)) + list.Add(obj); + foreach (var obj in list) + { + if (obj != null && obj.Value == null || + obj != null && Policy.IdleTimeout > TimeSpan.Zero && DateTime.Now.Subtract(obj.LastReturnTime) > Policy.IdleTimeout) + { + if (obj.Value != null) + { + Return(obj, true); + continue; + } + } + Return(obj); + } + } + /// /// 获取可用资源,或创建资源 /// diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 023e8c31..afe2ce8e 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -516,7 +516,9 @@ namespace FreeSql.Internal foreach (var col in trytb.Primarys) { col.Attribute.IsNullable = false; - col.Attribute.DbType = col.Attribute.DbType.Replace("NOT NULL", "").Replace(" NULL", "").Trim() + " NOT NULL"; //sqlite 主键也可以插入 null + col.Attribute.DbType = col.Attribute.DbType.Replace("NOT NULL", "").Replace(" NULL", "").Trim(); + if (common._orm.Ado.DataType == DataType.Sqlite) + col.Attribute.DbType += " NOT NULL"; //sqlite 主键也可以插入 null } foreach (var col in trytb.Columns.Values) { @@ -1544,6 +1546,13 @@ namespace FreeSql.Internal case DataType.GBase: if (dr.IsDBNull(index)) return null; break; + case DataType.MySql: + if (dr.GetFieldType(index).FullName == "MySqlConnector.MySqlDateTime") + { + if (dr.IsDBNull(index)) return null; + return dr.GetDateTime(index); + } + break; } return dr.GetValue(index); } diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs index 81cd6d7b..363f8d84 100644 --- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs +++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs @@ -12,43 +12,76 @@ using ClickHouse.Client.ADO; namespace FreeSql.ClickHouse { - class ClickHouseCodeFirst : Internal.CommonProvider.CodeFirstProvider { + public ClickHouseCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, + commonUtils, commonExpression) + { + } - public ClickHouseCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { } - static object _dicCsToDbLock = new object(); - - static Dictionary> _dicCsToDb = new Dictionary>() { - { typeof(bool).FullName, CsToDb.New(DbType.SByte, "Int8","Int8", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Int8","Nullable(Int8)", null, true, null) }, - - { typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", false, true, null) }, - { typeof(short).FullName, CsToDb.New(DbType.Int16, "Int16","Int16", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, "Int16", "Nullable(Int16)", false, true, null) }, - { typeof(int).FullName, CsToDb.New(DbType.Int32, "Int32", "Int32", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, "Int32", "Nullable(Int32)", false, true, null) }, - { typeof(long).FullName, CsToDb.New(DbType.Int64, "Int64","Int64", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, "Int64","Nullable(Int64)", false, true, null) }, - { typeof(byte).FullName, CsToDb.New(DbType.Byte, "UInt8","UInt8", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, "UInt8","Nullable(UInt8)", true, true, null) }, - { typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "UInt16","UInt16", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "UInt16", "Nullable(UInt16)", true, true, null) }, - { typeof(uint).FullName, CsToDb.New(DbType.UInt32, "UInt32", "UInt32", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "UInt32", "Nullable(UInt32)", true, true, null) }, - { typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "UInt64", "UInt64", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "UInt64", "Nullable(UInt64)", true, true, null) }, + static Dictionary> _dicCsToDb = new Dictionary>() + { + { typeof(bool).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", null, false, false) }, + { typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", null, true, null) }, - { typeof(double).FullName, CsToDb.New(DbType.Double, "Float64", "Float64", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, "Float64", "Nullable(Float64)", false, true, null) }, - { typeof(float).FullName, CsToDb.New(DbType.Single, "Float32","Float32", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, "Float32","Nullable(Float32)", false, true, null) }, - { typeof(decimal).FullName, CsToDb.New(DbType.Decimal, "Decimal128(19)","Decimal128(19)", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, "Nullable(Decimal128(19))","Nullable(Decimal128(19))", false, true, null) }, + { typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", false, false, 0) }, + { typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", false, true, null) }, + { typeof(short).FullName, CsToDb.New(DbType.Int16, "Int16", "Int16", false, false, 0) }, + { typeof(short?).FullName, CsToDb.New(DbType.Int16, "Int16", "Nullable(Int16)", false, true, null) }, + { typeof(int).FullName, CsToDb.New(DbType.Int32, "Int32", "Int32", false, false, 0) }, + { typeof(int?).FullName, CsToDb.New(DbType.Int32, "Int32", "Nullable(Int32)", false, true, null) }, + { typeof(long).FullName, CsToDb.New(DbType.Int64, "Int64", "Int64", false, false, 0) }, + { typeof(long?).FullName, CsToDb.New(DbType.Int64, "Int64", "Nullable(Int64)", false, true, null) }, - { typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "DateTime('Asia/Shanghai')", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "Nullable(DateTime('Asia/Shanghai'))", false, true, null) }, + { typeof(byte).FullName, CsToDb.New(DbType.Byte, "UInt8", "UInt8", true, false, 0) }, + { typeof(byte?).FullName, CsToDb.New(DbType.Byte, "UInt8", "Nullable(UInt8)", true, true, null) }, + { typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "UInt16", "UInt16", true, false, 0) }, + { typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "UInt16", "Nullable(UInt16)", true, true, null) }, + { typeof(uint).FullName, CsToDb.New(DbType.UInt32, "UInt32", "UInt32", true, false, 0) }, + { typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "UInt32", "Nullable(UInt32)", true, true, null) }, + { typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "UInt64", "UInt64", true, false, 0) }, + { typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "UInt64", "Nullable(UInt64)", true, true, null) }, - { typeof(string).FullName, CsToDb.New(DbType.String, "String", "String", false, null, "") }, - { typeof(char).FullName, CsToDb.New(DbType.String, "String", "String", false, false, "") },{ typeof(char?).FullName, CsToDb.New(DbType.Single, "String","Nullable(String)", false, true, null) }, - { typeof(Guid).FullName, CsToDb.New(DbType.String, "String", "String", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(DbType.String, "String", "Nullable(String)", false, true, null) }, + { typeof(double).FullName, CsToDb.New(DbType.Double, "Float64", "Float64", false, false, 0) }, + { typeof(double?).FullName, CsToDb.New(DbType.Double, "Float64", "Nullable(Float64)", false, true, null) }, + { typeof(float).FullName, CsToDb.New(DbType.Single, "Float32", "Float32", false, false, 0) }, + { typeof(float?).FullName, CsToDb.New(DbType.Single, "Float32", "Nullable(Float32)", false, true, null) }, + { + typeof(decimal).FullName, + CsToDb.New(DbType.Decimal, "Decimal(38, 19)", "Decimal(38, 19)", false, false, 0) //Nullable(Decimal(38, 19)) + }, + { + typeof(decimal?).FullName, + CsToDb.New(DbType.Decimal, "Nullable(Decimal(38, 19))", "Nullable(Decimal(38, 19))", false, true, null) + }, - }; + { + typeof(DateTime).FullName, + CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "DateTime('Asia/Shanghai')", false, false, + new DateTime(1970, 1, 1)) + }, + { + typeof(DateTime?).FullName, + CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "Nullable(DateTime('Asia/Shanghai'))", false, + true, null) + }, + + { typeof(string).FullName, CsToDb.New(DbType.String, "String", "String", false, null, "") }, + { typeof(char).FullName, CsToDb.New(DbType.String, "String", "String", false, false, "") }, + { typeof(char?).FullName, CsToDb.New(DbType.Single, "String", "Nullable(String)", false, true, null) }, + { typeof(Guid).FullName, CsToDb.New(DbType.String, "String", "String", false, false, Guid.Empty) }, + { typeof(Guid?).FullName, CsToDb.New(DbType.String, "String", "Nullable(String)", false, true, null) }, + }; public override DbInfoResult GetDbInfo(Type type) { - if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue); - if (type.IsArray) return null; + if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) + return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, + trydc.defaultValue); + if (type.IsArray) + return null; return null; } @@ -56,7 +89,7 @@ namespace FreeSql.ClickHouse { Object conn = null; string database = null; - + try { conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)); @@ -65,19 +98,25 @@ namespace FreeSql.ClickHouse var sb = new StringBuilder(); foreach (var obj in objects) { - if (sb.Length > 0) sb.Append("\r\n"); + if (sb.Length > 0) + sb.Append("\r\n"); var tb = _commonUtils.GetTableByEntity(obj.entityType); - if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName)); - if (tb.Columns.Any() == false) throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName)); + if (tb == null) + throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName)); + if (tb.Columns.Any() == false) + throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName)); var tbname = _commonUtils.SplitTableName(tb.DbName); - if (tbname?.Length == 1) tbname = new[] { database, tbname[0] }; + if (tbname?.Length == 1) + tbname = new[] { database, tbname[0] }; var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名 - if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] }; + if (tboldname?.Length == 1) + tboldname = new[] { database, tboldname[0] }; if (string.IsNullOrEmpty(obj.tableName) == false) { var tbtmpname = _commonUtils.SplitTableName(obj.tableName); - if (tbtmpname?.Length == 1) tbtmpname = new[] { database, tbtmpname[0] }; + if (tbtmpname?.Length == 1) + tbtmpname = new[] { database, tbtmpname[0] }; if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1]) { tbname = tbtmpname; @@ -85,20 +124,32 @@ namespace FreeSql.ClickHouse } } - if (string.Compare(tbname[0], database, true) != 0 && LocalExecuteScalar(database, _commonUtils.FormatSql(" select 1 from system.databases d where name={0}", tbname[0])) == null) //创建数据库 - sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(" ENGINE=Ordinary;\r\n"); + if (string.Compare(tbname[0], database, true) != 0 && LocalExecuteScalar(database, + _commonUtils.FormatSql(" select 1 from system.databases d where name={0}", tbname[0])) == + null) //创建数据库 + sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0])) + .Append(" ENGINE=Ordinary;\r\n"); var sbalter = new StringBuilder(); var istmpatler = false; //创建临时表,导入数据,删除旧表,修改 - if (LocalExecuteScalar(tbname[0], _commonUtils.FormatSql(" SELECT 1 FROM system.tables t WHERE database ={0} and name ={1}", tbname)) == null) - { //表不存在 + if (LocalExecuteScalar(tbname[0], + _commonUtils.FormatSql(" SELECT 1 FROM system.tables t WHERE database ={0} and name ={1}", + tbname)) == null) + { + //表不存在 if (tboldname != null) { - if (string.Compare(tboldname[0], tbname[0], true) != 0 && LocalExecuteScalar(database, _commonUtils.FormatSql(" select 1 from system.databases where name={0}", tboldname[0])) == null || - LocalExecuteScalar(tboldname[0], _commonUtils.FormatSql(" SELECT 1 FROM system.tables WHERE database={0} and name={1}", tboldname)) == null) + if (string.Compare(tboldname[0], tbname[0], true) != 0 && LocalExecuteScalar(database, + _commonUtils.FormatSql(" select 1 from system.databases where name={0}", + tboldname[0])) == null || + LocalExecuteScalar(tboldname[0], + _commonUtils.FormatSql( + " SELECT 1 FROM system.tables WHERE database={0} and name={1}", tboldname)) == + null) //数据库或表不存在 tboldname = null; } + if (tboldname == null) { //创建表 @@ -106,33 +157,44 @@ namespace FreeSql.ClickHouse sb.Append("CREATE TABLE IF NOT EXISTS ").Append(createTableName).Append(" ( "); foreach (var tbcol in tb.ColumnsByPosition) { - tbcol.Attribute.DbType = tbcol.Attribute.DbType.Replace(" NOT NULL", ""); - sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType); - if (string.IsNullOrEmpty(tbcol.Comment) == false) sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)); + //如果这里是主键就是不为Nullable + tbcol.Attribute.DbType = + CkNullableAdapter(tbcol.Attribute.DbType, tbcol.Attribute.IsPrimary); + tbcol.Attribute.DbType = CkIntAdapter(tbcol.Attribute.DbType); + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ") + .Append(tbcol.Attribute.DbType); + if (string.IsNullOrEmpty(tbcol.Comment) == false) + sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)); sb.Append(","); } foreach (var uk in tb.Indexes) { sb.Append(" \r\n "); - sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))); + sb.Append("INDEX ") + .Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))); + + sb.Append(" ("); foreach (var tbcol in uk.Columns) { - sb.Append(" "); sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); - sb.Append("TYPE set(8192) GRANULARITY 5, "); + sb.Append(", "); } - sb.Remove(sb.Length - 2, 2); + + sb.Remove(sb.Length - 2, 2).Append(") "); + sb.Append("TYPE set(8192) GRANULARITY 5,"); } + sb.Remove(sb.Length - 1, 1); sb.Append("\r\n) "); sb.Append("\r\nENGINE = MergeTree()"); - + if (tb.Primarys.Any()) { sb.Append(" \r\nORDER BY ( "); var ls = new StringBuilder(); - foreach (var tbcol in tb.Primarys) ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); + foreach (var tbcol in tb.Primarys) + ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); sb.Append(ls); sb.Remove(sb.Length - 2, 2); sb.Append(" )"); @@ -140,15 +202,20 @@ namespace FreeSql.ClickHouse sb.Append(ls); sb.Remove(sb.Length - 2, 2).Append(","); } + sb.Remove(sb.Length - 1, 1); //if (string.IsNullOrEmpty(tb.Comment) == false) // sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment)); sb.Append(" SETTINGS index_granularity = 8192;\r\n"); continue; } + //如果新表,旧表在一个数据库下,直接修改表名 if (string.Compare(tbname[0], tboldname[0], true) == 0) - sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1])).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n"); + sbalter.Append("RENAME TABLE ") + .Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1])) + .Append(" TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])) + .Append(";\r\n"); else { //如果新表,旧表不在一起,创建新表,导入数据,删除旧表 @@ -178,64 +245,162 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); { column = string.Concat(a[0]), sqlType = (string)a[1], - is_nullable = string.Concat(a[2]) == "1", + is_nullable = a[1]?.ToString().Contains("Nullable"), is_identity = false, comment = string.Concat(a[3]), - is_primary= string.Concat(a[6]) == "1", + is_primary = string.Concat(a[6]) == "1", }; }, StringComparer.CurrentCultureIgnoreCase); + if (istmpatler == false) { var existsPrimary = tbstruct.Any(o => o.Value.is_primary); + //对比列 foreach (var tbcol in tb.ColumnsByPosition) { - if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) || - string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) + //表中有这个字段 + var condition1 = tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol); + var condition2 = string.IsNullOrEmpty(tbcol.Attribute.OldName) == false; + if (condition1 || + condition2 && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) { var isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? ""); - if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false || - tbcol.Attribute.IsNullable != tbstructcol.is_nullable || isCommentChanged) - sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(tbcol.Attribute.IsNullable ? $"Nullable({tbcol.Attribute.DbType.Split(' ').First()})":tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n"); - if(isCommentChanged) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" COMMENT COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "")).Append(";\r\n"); + var ckDbType = CkNullableAdapter(tbcol.Attribute.DbType, tbcol.Attribute.IsPrimary); + ckDbType = CkIntAdapter(ckDbType); + var typeCondition1 = RemoveSpaceComparison(tbstructcol.sqlType, ckDbType) == false; + var typeCondition2 = tbcol.Attribute.IsNullable != tbstructcol.is_nullable; + if (typeCondition1 || typeCondition1 || isCommentChanged) + { + sbalter.Append("ALTER TABLE ") + .Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")) + .Append(" MODIFY COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)) + .Append(tbcol.Attribute.IsNullable && + tbcol.Attribute.DbType.Contains("Nullable") == false + ? $"Nullable({tbcol.Attribute.DbType.Split(' ').First()})" + : tbcol.Attribute.DbType.Split(' ').First()) + .Append(";\r\n"); + } + + if (isCommentChanged) + sbalter.Append("ALTER TABLE ") + .Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")) + .Append(" COMMENT COLUMN ") + .Append(_commonUtils.QuoteSqlName(tbstructcol.column)) + .Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "")).Append(";\r\n"); if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0) - sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(";\r\n"); + sbalter.Append("ALTER TABLE ") + .Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")) + .Append(" RENAME COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)) + .Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)) + .Append(";\r\n"); continue; } + //添加列 - sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" ADD ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType); - if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false) sbalter.Append(" DEFAULT ").Append(tbcol.DbDefaultValue); - if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "")); + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])) + .Append(" ADD Column ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ") + .Append(tbcol.Attribute.DbType); + if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && + tbcol.Attribute.IsIdentity == false) + sbalter.Append(" DEFAULT ").Append(tbcol.DbDefaultValue); + if (string.IsNullOrEmpty(tbcol.Comment) == false) + sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "")); sbalter.Append(";\r\n"); } + var indexSelectSql = _commonUtils.FormatSql( + @"SELECT name,expr FROM system.data_skipping_indices WHERE database={0}", + tboldname ?? tbname); + var indexCollect = _orm.Ado.Query(CommandType.Text, indexSelectSql); + //对比索引 + foreach (var uk in tb.Indexes) + { + if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) + continue; + var ukname = ReplaceIndexName(uk.Name, tbname[1]); + //先判断表中有没此字段的索引 + if (indexCollect.Any(c => + RemoveSpaceComparison(c.expr, + string.Join(',', uk.Columns.Select(i => i.Column.CsName))))) + { + //有这个字段的索引,但是名称不一样 修改名 , ClickHouse不支持修改列 + //if (!indexCollect.Where(c => c.name == uk.Name).Any()) + //{ + // + // sbalter.Append("ALTER TABLE ") + // .Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])) + // .Append(" DROP INDEX ").Append(ukname).Append(" ").Append(";\r\n"); + + // //添加 + // sbalter.Append("ALTER TABLE ") + // .Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])) + // .Append(" ADD INDEX ").Append(ukname).Append(" ("); + // foreach (var tbcol in uk.Columns) + // { + // sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); + // sbalter.Append(", "); + // } + + // sbalter.Remove(sbalter.Length - 2, 2).Append(") TYPE set(8192) GRANULARITY 5") + // .Append(";\r\n"); + //} + } + else + { + //创建索引 + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])) + .Append(" ADD INDEX ").Append(ukname).Append(" ("); + foreach (var tbcol in uk.Columns) + { + sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); + sbalter.Append(", "); + } + + sbalter.Remove(sbalter.Length - 2, 2).Append(") TYPE set(8192) GRANULARITY 5") + .Append(";\r\n"); + } + } } + if (istmpatler == false) + { + sb.Append(sbalter); + Console.WriteLine(sb.ToString()); + continue; + } //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名 - var tablename = tboldname == null ? _commonUtils.QuoteSqlName(tbname[0], tbname[1]) : _commonUtils.QuoteSqlName(tboldname[0], tboldname[1]); + var tablename = tboldname == null + ? _commonUtils.QuoteSqlName(tbname[0], tbname[1]) + : _commonUtils.QuoteSqlName(tboldname[0], tboldname[1]); var tmptablename = _commonUtils.QuoteSqlName(tbname[0], $"FreeSqlTmp_{tbname[1]}"); //创建临时表 sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( "); foreach (var tbcol in tb.ColumnsByPosition) { tbcol.Attribute.DbType = tbcol.Attribute.DbType.Replace(" NOT NULL", ""); - sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType); - if (string.IsNullOrEmpty(tbcol.Comment) == false) sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)); + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ") + .Append(tbcol.Attribute.DbType); + if (string.IsNullOrEmpty(tbcol.Comment) == false) + sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)); sb.Append(","); } foreach (var uk in tb.Indexes) { sb.Append(" \r\n "); - sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append("("); + sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))) + .Append("("); foreach (var tbcol in uk.Columns) { sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); sb.Append("TYPE set(8192) GRANULARITY 5, "); } + sb.Remove(sb.Length - 2, 2).Append("),"); } + sb.Remove(sb.Length - 1, 1); sb.Append("\r\n) "); sb.Append("\r\nENGINE = MergeTree()"); @@ -244,7 +409,8 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); { sb.Append(" \r\nORDER BY ( "); var ls = new StringBuilder(); - foreach (var tbcol in tb.Primarys) ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); + foreach (var tbcol in tb.Primarys) + ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); sb.Append(ls); sb.Remove(sb.Length - 2, 2); sb.Append(" )"); @@ -252,6 +418,7 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); sb.Append(ls); sb.Remove(sb.Length - 2, 2).Append(","); } + sb.Remove(sb.Length - 1, 1); //if (string.IsNullOrEmpty(tb.Comment) == false) // sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment)); @@ -262,25 +429,32 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); { var insertvalue = "NULL"; if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) || - string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) + string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && + tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol)) { insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column); - if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false) + if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, + StringComparison.CurrentCultureIgnoreCase) == false) { //insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})"; } + if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable) insertvalue = $"ifnull({insertvalue},{tbcol.DbDefaultValue})"; } else if (tbcol.Attribute.IsNullable == false) if (tbcol.DbDefaultValue != "NULL") insertvalue = tbcol.DbDefaultValue; + sb.Append(insertvalue).Append(", "); } + sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n"); sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n"); - sb.Append("RENAME TABLE ").Append(tmptablename).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n"); + sb.Append("RENAME TABLE ").Append(tmptablename).Append(" TO ") + .Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n"); } + return sb.Length == 0 ? null : sb.ToString(); } finally @@ -299,7 +473,8 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); object LocalExecuteScalar(string db, string sql) { - if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db); + if (string.Compare(database, db) != 0) + conn.Value.ChangeDatabase(db); try { using (var cmd = conn.Value.CreateCommand()) @@ -311,17 +486,72 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); } finally { - if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database); + if (string.Compare(database, db) != 0) + conn.Value.ChangeDatabase(database); } } + + string CkNullablePrimaryAdapter(string dbType, bool isPrimary) + { + return isPrimary + ? dbType.Replace("Nullable(", "").Replace(")", "") + : dbType.Replace(" NOT NULL", ""); + } + string CkNullableAdapter(string dbType, bool isPrimary) + { + return isPrimary + ? dbType.Replace("Nullable(", "").Replace(")","").Replace(" NOT NULL", "") + : dbType.Replace(" NOT NULL", ""); + } + + + string CkIntAdapter(string dbType) + { + var result = dbType; + if (dbType.ToLower().Contains("int64")) + { + if (dbType.Contains("Nullable")) + { + result = "Nullable(Int64)"; + } + else + { + result = "Int64"; + } + } + else if (dbType.ToLower().Contains("int")) + { + if (dbType.Contains("Nullable")) + { + result = "Nullable(Int32)"; + } + else + { + result = "Int32"; + } + } + + return result; + } + + //去除空格后比较 + bool RemoveSpaceComparison(string a, string b) + { + a = Regex.Replace(a, @"\s", "").ToLower(); + b = Regex.Replace(b, @"\s", "").ToLower(); + return a == b; + } } public override int ExecuteDDLStatements(string ddl) { - if (string.IsNullOrEmpty(ddl)) return 0; - var scripts = ddl.Split(new string[] { ";\r\n" }, StringSplitOptions.None).Where(a => string.IsNullOrEmpty(a.Trim()) == false).ToArray(); + if (string.IsNullOrEmpty(ddl)) + return 0; + var scripts = ddl.Split(new string[] { ";\r\n" }, StringSplitOptions.None) + .Where(a => string.IsNullOrEmpty(a.Trim()) == false).ToArray(); - if (scripts.Any() == false) return 0; + if (scripts.Any() == false) + return 0; var affrows = 0; foreach (var script in scripts) @@ -329,4 +559,16 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname); return affrows; } } + + internal class ClickHouseTableIndex + { + public string name + { + get; set; + } + public string expr + { + get; set; + } + } } \ No newline at end of file diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs index 4a40da88..126b0704 100644 --- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs +++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs @@ -79,7 +79,7 @@ namespace FreeSql.ClickHouse public override string FormatSql(string sql, params object[] args) => sql?.FormatClickHouse(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs index 795a93d1..e0988d57 100644 --- a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs +++ b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs @@ -46,7 +46,7 @@ namespace FreeSql.ClickHouse.Curd //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1 for (var b = 1; b < tbsfrom.Length; b++) { - sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias); + sb.Append(" \r\nGLOBAL LEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias); if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1"); else @@ -80,13 +80,13 @@ namespace FreeSql.ClickHouse.Curd case SelectTableInfoType.RawJoin: continue; case SelectTableInfoType.LeftJoin: - sb.Append(" \r\nLEFT JOIN "); + sb.Append(" \r\nGLOBAL LEFT JOIN "); break; case SelectTableInfoType.InnerJoin: - sb.Append(" \r\nINNER JOIN "); + sb.Append(" \r\nGLOBAL INNER JOIN "); break; case SelectTableInfoType.RightJoin: - sb.Append(" \r\nRIGHT JOIN "); + sb.Append(" \r\nGLOBAL RIGHT JOIN "); break; } sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition); diff --git a/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj b/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj index bb102b91..fd181fe5 100644 --- a/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj +++ b/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj @@ -19,7 +19,7 @@ False key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Custom/CustomUtils.cs b/Providers/FreeSql.Provider.Custom/CustomUtils.cs index c3a99a4c..8e18320f 100644 --- a/Providers/FreeSql.Provider.Custom/CustomUtils.cs +++ b/Providers/FreeSql.Provider.Custom/CustomUtils.cs @@ -41,7 +41,7 @@ namespace FreeSql.Custom static FreeSql.Custom.CustomAdo _customAdo = new FreeSql.Custom.CustomAdo(); public override string FormatSql(string sql, params object[] args) => (_orm?.Ado as CustomAdo)?.Addslashes(sql, args) ?? _customAdo.Addslashes(sql, args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj b/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj index 61f41945..409df27a 100644 --- a/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj +++ b/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Custom/MySql/CustomMySqlUtils.cs b/Providers/FreeSql.Provider.Custom/MySql/CustomMySqlUtils.cs index 2cdf0a46..82db8cfc 100644 --- a/Providers/FreeSql.Provider.Custom/MySql/CustomMySqlUtils.cs +++ b/Providers/FreeSql.Provider.Custom/MySql/CustomMySqlUtils.cs @@ -41,7 +41,7 @@ namespace FreeSql.Custom.MySql }); public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomMySql(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Custom/Oracle/CustomOracleUtils.cs b/Providers/FreeSql.Provider.Custom/Oracle/CustomOracleUtils.cs index f1950fc7..1d31435d 100644 --- a/Providers/FreeSql.Provider.Custom/Oracle/CustomOracleUtils.cs +++ b/Providers/FreeSql.Provider.Custom/Oracle/CustomOracleUtils.cs @@ -71,7 +71,7 @@ namespace FreeSql.Custom.Oracle }); public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomOracle(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Custom/PostgreSQL/CustomPostgreSQLUtils.cs b/Providers/FreeSql.Provider.Custom/PostgreSQL/CustomPostgreSQLUtils.cs index 7e83cfa8..e59fd9e8 100644 --- a/Providers/FreeSql.Provider.Custom/PostgreSQL/CustomPostgreSQLUtils.cs +++ b/Providers/FreeSql.Provider.Custom/PostgreSQL/CustomPostgreSQLUtils.cs @@ -97,7 +97,7 @@ namespace FreeSql.Custom.PostgreSQL }); public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomPostgreSQL(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Custom/SqlServer/CustomSqlServerUtils.cs b/Providers/FreeSql.Provider.Custom/SqlServer/CustomSqlServerUtils.cs index 0a78ff34..9bb034d7 100644 --- a/Providers/FreeSql.Provider.Custom/SqlServer/CustomSqlServerUtils.cs +++ b/Providers/FreeSql.Provider.Custom/SqlServer/CustomSqlServerUtils.cs @@ -49,7 +49,7 @@ namespace FreeSql.Custom.SqlServer }); public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomSqlServer(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Dameng/DamengUtils.cs b/Providers/FreeSql.Provider.Dameng/DamengUtils.cs index cfee3da5..ce0c1ed2 100644 --- a/Providers/FreeSql.Provider.Dameng/DamengUtils.cs +++ b/Providers/FreeSql.Provider.Dameng/DamengUtils.cs @@ -82,7 +82,7 @@ namespace FreeSql.Dameng }); public override string FormatSql(string sql, params object[] args) => sql?.FormatDameng(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj b/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj index ecbef1e4..4e0b6a00 100644 --- a/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj +++ b/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Firebird/FirebirdUtils.cs b/Providers/FreeSql.Provider.Firebird/FirebirdUtils.cs index 2655e2f0..ba07d24d 100644 --- a/Providers/FreeSql.Provider.Firebird/FirebirdUtils.cs +++ b/Providers/FreeSql.Provider.Firebird/FirebirdUtils.cs @@ -52,7 +52,7 @@ namespace FreeSql.Firebird }); public override string FormatSql(string sql, params object[] args) => sql?.FormatFirebird(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj b/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj index 36e7bf47..a41d2e83 100644 --- a/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj +++ b/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj b/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj index 9ea9cdf0..61330501 100644 --- a/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj +++ b/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.GBase/GBaseUtils.cs b/Providers/FreeSql.Provider.GBase/GBaseUtils.cs index b2c243ca..c59177f2 100644 --- a/Providers/FreeSql.Provider.GBase/GBaseUtils.cs +++ b/Providers/FreeSql.Provider.GBase/GBaseUtils.cs @@ -56,7 +56,7 @@ namespace FreeSql.GBase }); public override string FormatSql(string sql, params object[] args) => sql?.FormatGBase(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj b/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj index 925621c6..f2f433d4 100644 --- a/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj +++ b/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs index f70ad987..3dba7e74 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs @@ -92,7 +92,7 @@ namespace FreeSql.KingbaseES }); public override string FormatSql(string sql, params object[] args) => sql?.FormatKingbaseES(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj b/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj index 00b81f1c..304d4d83 100644 --- a/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj +++ b/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.MsAccess/MsAccessUtils.cs b/Providers/FreeSql.Provider.MsAccess/MsAccessUtils.cs index 35ca64fe..bdb5046e 100644 --- a/Providers/FreeSql.Provider.MsAccess/MsAccessUtils.cs +++ b/Providers/FreeSql.Provider.MsAccess/MsAccessUtils.cs @@ -38,7 +38,7 @@ namespace FreeSql.MsAccess }); public override string FormatSql(string sql, params object[] args) => sql?.FormatAccess(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj b/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj index f3b4a829..f2a12a4d 100644 --- a/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj +++ b/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.MySql/MySqlProvider.cs b/Providers/FreeSql.Provider.MySql/MySqlProvider.cs index f1ed6f12..42cdb1cc 100644 --- a/Providers/FreeSql.Provider.MySql/MySqlProvider.cs +++ b/Providers/FreeSql.Provider.MySql/MySqlProvider.cs @@ -36,6 +36,7 @@ namespace FreeSql.MySql }); Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) }); + Select0Provider._dicMethodDataReaderGetValue[typeof(DateTimeOffset)] = typeof(DbDataReader).GetMethod("GetDateTime", new Type[] { typeof(int) }); } public override ISelect CreateSelectProvider(object dywhere) => new MySqlSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); diff --git a/Providers/FreeSql.Provider.MySql/MySqlUtils.cs b/Providers/FreeSql.Provider.MySql/MySqlUtils.cs index 7ca8d271..a1622cc6 100644 --- a/Providers/FreeSql.Provider.MySql/MySqlUtils.cs +++ b/Providers/FreeSql.Provider.MySql/MySqlUtils.cs @@ -73,7 +73,7 @@ namespace FreeSql.MySql }); public override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj b/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj index a6af21bc..e0c2a6bd 100644 --- a/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj +++ b/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs b/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs index ccb83ad1..0cbb6324 100644 --- a/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs +++ b/Providers/FreeSql.Provider.MySqlConnector/FreeSqlMySqlConnectorGlobalExtensions.cs @@ -4,6 +4,11 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using System.Threading; +using FreeSql.Internal.Model; +using FreeSql.Internal.CommonProvider; +using FreeSql.Internal.ObjectPool; +using System.Linq; +using System.Data.Common; #if MySqlConnector using MySqlConnector; #else @@ -13,6 +18,55 @@ using MySql.Data.MySqlClient; public static class FreeSqlMySqlConnectorGlobalExtensions { #region ExecuteMySqlBulkCopy + + /// + /// 批量更新(更新字段数量超过 2000 时收益大) + /// 实现原理:使用 MySqlBulkCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新 + /// + /// + /// + /// + /// + public static int ExecuteMySqlBulkCopy(this IUpdate that, int? bulkCopyTimeout = null) where T : class + { + var update = that as UpdateProvider; + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0; + var state = ExecuteMySqlBulkCopyState(update); + return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecuteMySqlBulkCopy(bulkCopyTimeout)); + } + static NativeTuple ExecuteMySqlBulkCopyState(UpdateProvider update) where T : class + { + if (update._source.Any() != true) return null; + var _table = update._table; + var _commonUtils = update._commonUtils; + var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName; + var tempTableName = $"Temp_{Guid.NewGuid().ToString("N")}"; + if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower(); + if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper(); + if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null) + update.WithTransaction(update._orm.Ado.TransactionCurrentThread); + var sb = new StringBuilder().Append("CREATE TEMPORARY TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( "); + var setColumns = new List(); + var pkColumns = new List(); + foreach (var col in _table.Columns.Values) + { + if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name); + else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name); + else continue; + 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(); + + sb.Clear().Append("UPDATE ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ") + .Append(" \r\nINNER JOIN ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" b ON ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}"))) + .Append(" \r\nSET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}"))); + var sql2 = sb.ToString(); + sb.Clear(); + var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}"; + return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray()); + } + /// /// MySql MySqlCopyBulk 批量插入功能 /// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列 @@ -93,6 +147,13 @@ public static class FreeSqlMySqlConnectorGlobalExtensions } #if net40 #else + public static Task ExecuteMySqlBulkCopyAsync(this IUpdate that, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class + { + var update = that as UpdateProvider; + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0); + var state = ExecuteMySqlBulkCopyState(update); + return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecuteMySqlBulkCopyAsync(bulkCopyTimeout, cancellationToken)); + } async public static Task ExecuteMySqlBulkCopyAsync(this IInsert that, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class { var insert = that as FreeSql.MySql.Curd.MySqlInsert; diff --git a/Providers/FreeSql.Provider.MySqlConnector/MySqlConnectorUtils.cs b/Providers/FreeSql.Provider.MySqlConnector/MySqlConnectorUtils.cs index 7a7b8084..ad08572d 100644 --- a/Providers/FreeSql.Provider.MySqlConnector/MySqlConnectorUtils.cs +++ b/Providers/FreeSql.Provider.MySqlConnector/MySqlConnectorUtils.cs @@ -91,7 +91,7 @@ namespace FreeSql.MySql } public override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengUtils.cs b/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengUtils.cs index 9ce96dbb..862decf2 100644 --- a/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/Dameng/OdbcDamengUtils.cs @@ -71,7 +71,7 @@ namespace FreeSql.Odbc.Dameng }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcDameng(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/Default/OdbcUtils.cs b/Providers/FreeSql.Provider.Odbc/Default/OdbcUtils.cs index 328bfe2a..b16ad50d 100644 --- a/Providers/FreeSql.Provider.Odbc/Default/OdbcUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/Default/OdbcUtils.cs @@ -39,7 +39,7 @@ namespace FreeSql.Odbc.Default static FreeSql.Odbc.Default.OdbcAdo _customAdo = new FreeSql.Odbc.Default.OdbcAdo(); public override string FormatSql(string sql, params object[] args) => (_orm?.Ado as OdbcAdo)?.Addslashes(sql, args) ?? _customAdo.Addslashes(sql, args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj b/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj index 0541f0b5..1ed4f3ed 100644 --- a/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj +++ b/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Odbc/KingbaseES/OdbcKingbaseESUtils.cs b/Providers/FreeSql.Provider.Odbc/KingbaseES/OdbcKingbaseESUtils.cs index c8a0b400..536b8b80 100644 --- a/Providers/FreeSql.Provider.Odbc/KingbaseES/OdbcKingbaseESUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/KingbaseES/OdbcKingbaseESUtils.cs @@ -91,7 +91,7 @@ namespace FreeSql.Odbc.KingbaseES }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcKingbaseES(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlUtils.cs b/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlUtils.cs index 0ec715b9..d78f43ea 100644 --- a/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/MySql/OdbcMySqlUtils.cs @@ -37,7 +37,7 @@ namespace FreeSql.Odbc.MySql }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcMySql(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleUtils.cs b/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleUtils.cs index a7920b0e..4530b4b7 100644 --- a/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/Oracle/OdbcOracleUtils.cs @@ -71,7 +71,7 @@ namespace FreeSql.Odbc.Oracle }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcOracle(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLUtils.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLUtils.cs index e5fab3d3..e9b463b6 100644 --- a/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/OdbcPostgreSQLUtils.cs @@ -91,7 +91,7 @@ namespace FreeSql.Odbc.PostgreSQL }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcPostgreSQL(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerUtils.cs b/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerUtils.cs index d98487d3..8513e0b2 100644 --- a/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerUtils.cs +++ b/Providers/FreeSql.Provider.Odbc/SqlServer/OdbcSqlServerUtils.cs @@ -43,7 +43,7 @@ namespace FreeSql.Odbc.SqlServer }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcSqlServer(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj b/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj index 46952429..2437cec0 100644 --- a/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj +++ b/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Oracle/OracleUtils.cs b/Providers/FreeSql.Provider.Oracle/OracleUtils.cs index 5fc1d27f..51661c0c 100644 --- a/Providers/FreeSql.Provider.Oracle/OracleUtils.cs +++ b/Providers/FreeSql.Provider.Oracle/OracleUtils.cs @@ -100,7 +100,7 @@ namespace FreeSql.Oracle }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOracle(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj b/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj index 2b5ca143..c2fbd732 100644 --- a/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj +++ b/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.OracleOledb/OracleOledbUtils.cs b/Providers/FreeSql.Provider.OracleOledb/OracleOledbUtils.cs index 77f6f3ef..edfa2da2 100644 --- a/Providers/FreeSql.Provider.OracleOledb/OracleOledbUtils.cs +++ b/Providers/FreeSql.Provider.OracleOledb/OracleOledbUtils.cs @@ -104,7 +104,7 @@ namespace FreeSql.Oracle }); public override string FormatSql(string sql, params object[] args) => sql?.FormatOracle(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj b/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj index 5a1aed23..9e009d72 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj +++ b/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs index 8f3b3179..a85c7166 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs +++ b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLExtensions.cs @@ -1,9 +1,13 @@ using FreeSql; +using FreeSql.Internal.CommonProvider; +using FreeSql.Internal.Model; using FreeSql.PostgreSQL.Curd; using Npgsql; using System; +using System.Collections.Generic; using System.Data; using System.Diagnostics; +using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading; @@ -32,6 +36,54 @@ public static partial class FreeSqlPostgreSQLGlobalExtensions public static OnConflictDoUpdate OnConflictDoUpdate(this IInsert that, Expression> columns = null) where T1 : class => new FreeSql.PostgreSQL.Curd.OnConflictDoUpdate(that.InsertIdentity(), columns); #region ExecutePgCopy + /// + /// 批量更新(更新字段数量超过 2000 时收益大) + /// 实现原理:使用 PgCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新 + /// + /// + /// + /// + public static int ExecutePgCopy(this IUpdate that) where T : class + { + var update = that as UpdateProvider; + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0; + var state = ExecutePgCopyState(update); + return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecutePgCopy()); + } + static NativeTuple ExecutePgCopyState(UpdateProvider update) where T : class + { + if (update._source.Any() != true) return null; + var _table = update._table; + var _commonUtils = update._commonUtils; + var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName; + var tempTableName = $"Temp_{Guid.NewGuid().ToString("N")}"; + if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower(); + if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper(); + if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null) + update.WithTransaction(update._orm.Ado.TransactionCurrentThread); + var sb = new StringBuilder().Append("CREATE TEMP TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( "); + var setColumns = new List(); + var pkColumns = new List(); + foreach (var col in _table.Columns.Values) + { + if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name); + else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name); + else continue; + 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) WITH (OIDS=FALSE);").ToString(); + + sb.Clear().Append("UPDATE ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ") + .Append("\r\nSET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}"))) + .Append("\r\nFROM ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" b ") + .Append("\r\nWHERE ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}"))); + var sql2 = sb.ToString(); + sb.Clear(); + var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}"; + return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray()); + } + /// /// PostgreSQL COPY 批量导入功能,封装了 NpgsqlConnection.BeginBinaryImport 方法 /// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列 @@ -121,6 +173,13 @@ public static partial class FreeSqlPostgreSQLGlobalExtensions #if net45 #else + public static Task ExecutePgCopyAsync(this IUpdate that, CancellationToken cancellationToken = default) where T : class + { + var update = that as UpdateProvider; + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0); + var state = ExecutePgCopyState(update); + return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecutePgCopyAsync(cancellationToken)); + } async public static Task ExecutePgCopyAsync(this IInsert that, CancellationToken cancellationToken = default) where T : class { var insert = that as FreeSql.PostgreSQL.Curd.PostgreSQLInsert; diff --git a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLUtils.cs b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLUtils.cs index 2fb59508..96ae3dfa 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLUtils.cs +++ b/Providers/FreeSql.Provider.PostgreSQL/PostgreSQLUtils.cs @@ -132,7 +132,7 @@ namespace FreeSql.PostgreSQL }); public override string FormatSql(string sql, params object[] args) => sql?.FormatPostgreSQL(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj b/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj index 03a733b7..ce48a8c2 100644 --- a/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj +++ b/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs index 001ff7e1..9e0db7e1 100644 --- a/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs @@ -105,7 +105,7 @@ namespace FreeSql.ShenTong }); public override string FormatSql(string sql, params object[] args) => sql?.FormatShenTong(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj b/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj index 9c18f3c6..c0c9f961 100644 --- a/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj +++ b/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj @@ -18,7 +18,7 @@ true false key.snk - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs b/Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs index 315ae260..91880a60 100644 --- a/Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs +++ b/Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs @@ -5,6 +5,10 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using FreeSql.Internal.CommonProvider; +using System.Linq; +using System.Text; +using System.Data.Common; +using FreeSql.Internal.ObjectPool; #if microsoft using Microsoft.Data.SqlClient; #else @@ -115,6 +119,51 @@ public static partial class FreeSqlSqlServerGlobalExtensions internal static ConcurrentDictionary>> _dicSetGlobalSelectWithLock = new ConcurrentDictionary>>(); #region ExecuteSqlBulkCopy + /// + /// 批量更新(更新字段数量超过 2000 时收益大) + /// 实现原理:使用 SqlBulkCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新 + /// + /// + /// + /// + /// + /// + /// + public static int ExecuteSqlBulkCopy(this IUpdate that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null) where T : class + { + var update = that as UpdateProvider; + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0; + var state = ExecuteSqlBulkCopyState(update); + return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecuteSqlBulkCopy(copyOptions, batchSize, bulkCopyTimeout)); + } + static NativeTuple ExecuteSqlBulkCopyState(UpdateProvider update) where T : class + { + if (update._source.Any() != true) return null; + var _table = update._table; + var _commonUtils = update._commonUtils; + var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName; + var tempTableName = $"#Temp_{updateTableName}"; + if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower(); + if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper(); + if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null) + update.WithTransaction(update._orm.Ado.TransactionCurrentThread); + var setColumns = new List(); + var pkColumns = new List(); + foreach (var col in _table.Columns.Values) + { + if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name); + else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name); + } + var sql1 = $"SELECT {string.Join(", ", pkColumns)}, {string.Join(", ", setColumns)} INTO {tempTableName} FROM {_commonUtils.QuoteSqlName(updateTableName)} WHERE 1=2"; + var sb = new StringBuilder().Append("UPDATE ").Append(" a SET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}"))); + sb.Append(" \r\nFROM ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ") + .Append(" \r\nINNER JOIN ").Append(tempTableName).Append(" b ON ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}"))); + var sql2 = sb.ToString(); + sb.Clear(); + var sql3 = $"DROP TABLE {tempTableName}"; + return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray()); + } + /// /// SqlServer SqlCopyBulk 批量插入功能 /// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列 @@ -214,6 +263,13 @@ public static partial class FreeSqlSqlServerGlobalExtensions } #if net40 #else + public static Task ExecuteSqlBulkCopyAsync(this IUpdate that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class + { + var update = that as UpdateProvider; + if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0); + var state = ExecuteSqlBulkCopyState(update); + return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecuteSqlBulkCopyAsync(copyOptions, batchSize, bulkCopyTimeout, cancellationToken)); + } 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; diff --git a/Providers/FreeSql.Provider.SqlServer/SqlServerUtils.cs b/Providers/FreeSql.Provider.SqlServer/SqlServerUtils.cs index 5a692876..987f0817 100644 --- a/Providers/FreeSql.Provider.SqlServer/SqlServerUtils.cs +++ b/Providers/FreeSql.Provider.SqlServer/SqlServerUtils.cs @@ -59,7 +59,7 @@ namespace FreeSql.SqlServer }); public override string FormatSql(string sql, params object[] args) => sql?.FormatSqlServer(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj b/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj index e9e9aa29..2d1eadc9 100644 --- a/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj +++ b/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj b/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj index f7f13124..8a53471e 100644 --- a/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj +++ b/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/Providers/FreeSql.Provider.Sqlite/SqliteUtils.cs b/Providers/FreeSql.Provider.Sqlite/SqliteUtils.cs index 631f6cb5..083df5aa 100644 --- a/Providers/FreeSql.Provider.Sqlite/SqliteUtils.cs +++ b/Providers/FreeSql.Provider.Sqlite/SqliteUtils.cs @@ -82,7 +82,7 @@ namespace FreeSql.Sqlite }); public override string FormatSql(string sql, params object[] args) => sql?.FormatSqlite(args); - public override string QuoteSqlName(params string[] name) + public override string QuoteSqlNameAdapter(params string[] name) { if (name.Length == 1) { diff --git a/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj b/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj index 368ad0d9..f38c5a97 100644 --- a/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj +++ b/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.684-preview20221124 + 3.2.686-preview20221226 diff --git a/README.md b/README.md index 0b8f0bfd..56d1d85f 100644 --- a/README.md +++ b/README.md @@ -210,13 +210,15 @@ constantine, [mafeng8](https://github.com/mafeng8), [VicBilibily](https://github.com/VicBilibily), [Soar](https://github.com/sgf), -[quzhen91](https://github.com/quzhen91) etc. +[quzhen91](https://github.com/quzhen91), +homejun, +[d4ilys](https://github.com/d4ilys) etc. ## 💕 Donation L*y 58元、花花 88元、麦兜很乖 50元、网络来者 2000元、John 99.99元、alex 666元、bacongao 36元、无名 100元、Eternity 188元、无名 10元、⌒.Helper~..oO 66元、习惯与被习惯 100元、无名 100元、蔡易喋 88.88元、中讯科技 1000元、Good Good Work 24元、炽焰 6.6元、Nothing 100元、兰州天擎赵 500元、哈利路亚 300元、 无名 100元、蛰伏 99.99元、TCYM 66.66元、MOTA 5元、LDZXG 30元、Near 30元、建爽 66元、无名 200元、LambertWu 100元、无名 18.88元、乌龙 50元、无名 100元、陳怼怼 66.66元、陳怼怼 66.66元、丁淮 100元、李伟坚-Excel催化剂 100元、白狐 6.66元、她微笑的脸y 30元、Eternity²º²¹ 588元、夜归柴门 88元、蔡易喋 666.66元、 -*礼 10元、litrpa 88元、Alax CHOW 200元、Daily 66元、k\*t 66元、蓝 100元、*菜 10元、生命如歌 1000元、山鸡 88元、平凡 100元、大树 1000元、软软的毛毛虫 66.66 +*礼 10元、litrpa 88元、Alax CHOW 200元、Daily 66元、k\*t 66元、蓝 100元、*菜 10元、生命如歌 1000元、山鸡 88元、平凡 100元、大树 1000元、软软的毛毛虫 66.66元、问卷星 2000元 > Thank you for your donation diff --git a/README.zh-CN.md b/README.zh-CN.md index 072e4001..541b3806 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -211,13 +211,15 @@ constantine, [mafeng8](https://github.com/mafeng8), [VicBilibily](https://github.com/VicBilibily), [Soar](https://github.com/sgf), -[quzhen91](https://github.com/quzhen91) 等。 +[quzhen91](https://github.com/quzhen91), +homejun, +[d4ilys](https://github.com/d4ilys) 等。 ## 💕 Donation (捐赠) L\*y 58元、花花 88元、麦兜很乖 50元、网络来者 2000元、John 99.99元、alex 666元、bacongao 36元、无名 100元、Eternity 188元、无名 10元、⌒.Helper~..oO 66元、习惯与被习惯 100元、无名 100元、蔡易喋 88.88元、中讯科技 1000元、Good Good Work 24元、炽焰 6.6元、Nothing 100元、兰州天擎赵 500元、哈利路亚 300元、 无名 100元、蛰伏 99.99元、TCYM 66.66元、MOTA 5元、LDZXG 30元、Near 30元、建爽 66元、无名 200元、LambertWu 100元、无名 18.88元、乌龙 50元、无名 100元、陳怼怼 66.66元、陳怼怼 66.66元、丁淮 100元、李伟坚-Excel催化剂 100元、白狐 6.66元、她微笑的脸y 30元、Eternity²º²¹ 588元、夜归柴门 88元、蔡易喋 666.66元、 -*礼 10元、litrpa 88元、Alax CHOW 200元、Daily 66元、k*t 66元、蓝 100元、\*菜 10元、生命如歌 1000元、山鸡 88元、平凡 100元、大树 1000元、软软的毛毛虫 66.66 +*礼 10元、litrpa 88元、Alax CHOW 200元、Daily 66元、k*t 66元、蓝 100元、\*菜 10元、生命如歌 1000元、山鸡 88元、平凡 100元、大树 1000元、软软的毛毛虫 66.66元、问卷星 2000元 > 超级感谢你的打赏。