From 49aa899f81ecec555ca13c5444c3775c3ce346f3 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Wed, 10 Jun 2020 02:07:55 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20=E7=A5=9E=E5=B7=9E?= =?UTF-8?q?=E9=80=9A=E7=94=A8=20ShenTong=20=E5=AE=9E=E7=8E=B0=EF=BC=9B#325?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.DbContext/DbSet/DbSet.cs | 1 + FreeSql.DbContext/DbSet/DbSetAsync.cs | 2 + FreeSql.DbContext/DbSet/DbSetSync.cs | 2 + FreeSql.DbContext/FreeSql.DbContext.xml | 16 - .../FreeSql.Tests/FreeSql.Tests.csproj | 7 + .../ShenTong/Curd/ShenTongDeleteTest.cs | 105 + .../Curd/ShenTongInsertOrUpdateTest.cs | 323 +++ .../ShenTong/Curd/ShenTongInsertTest.cs | 141 ++ .../ShenTong/Curd/ShenTongSelectTest.cs | 1863 +++++++++++++++++ .../ShenTong/Curd/ShenTongUpdateTest.cs | 189 ++ .../ShenTong/MapType/BoolNullableTest.cs | 1571 ++++++++++++++ .../ShenTong/MapType/BoolTest.cs | 1105 ++++++++++ .../ShenTong/MapType/DateTimeOffSetTest.cs | 54 + .../ShenTong/MapType/EnumTest.cs | 261 +++ .../ShenTong/MapType/ToStringTest.cs | 570 +++++ .../ShenTong/ShenTongAdo/ShenTongAdoTest.cs | 68 + .../FreeSql.Tests/ShenTong/ShenTongAopTest.cs | 40 + .../ShenTong/ShenTongCodeFirstTest.cs | 348 +++ .../ShenTong/ShenTongDbFirstTest.cs | 25 + .../ShenTongExpression/ConvertTest.cs | 169 ++ .../ShenTongExpression/DateTimeTest.cs | 348 +++ .../ShenTong/ShenTongExpression/MathTest.cs | 156 ++ .../ShenTong/ShenTongExpression/OtherTest.cs | 221 ++ .../ShenTong/ShenTongExpression/StringTest.cs | 728 +++++++ .../ShenTongExpression/TimeSpanTest.cs | 293 +++ FreeSql.Tests/FreeSql.Tests/g.cs | 26 + FreeSql.sln | 17 +- FreeSql/DataType.cs | 7 +- FreeSql/FreeSql.xml | 8 +- FreeSql/FreeSqlBuilder.cs | 5 + FreeSql/Interface/Curd/ISelect/ISelect0.cs | 3 +- .../SelectProvider/Select0Provider.cs | 5 +- FreeSql/Internal/UtilsExpressionTree.cs | 1 + .../Curd/OnConflictDoUpdate.cs | 207 ++ .../Curd/ShenTongDelete.cs | 99 + .../Curd/ShenTongInsert.cs | 216 ++ .../Curd/ShenTongInsertOrUpdate.cs | 72 + .../Curd/ShenTongSelect.cs | 174 ++ .../Curd/ShenTongUpdate.cs | 164 ++ .../FreeSql.Provider.ShenTong.csproj | 52 + .../ShenTongAdo/ShenTongAdo.cs | 77 + .../ShenTongAdo/ShenTongConnectionPool.cs | 247 +++ .../ShenTongCodeFirst.cs | 412 ++++ .../ShenTongDbFirst.cs | 513 +++++ .../ShenTongExpression.cs | 571 +++++ .../ShenTongExtensions.cs | 17 + .../ShenTongProvider.cs | 64 + .../ShenTongUtils.cs | 173 ++ Providers/FreeSql.Provider.ShenTong/key.snk | Bin 0 -> 596 bytes .../lib/Mono.Security.dll | Bin 0 -> 282624 bytes .../lib/System.Data.OscarClient.dll | Bin 0 -> 454656 bytes 51 files changed, 11714 insertions(+), 22 deletions(-) create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongDeleteTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertOrUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongSelectTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongUpdateTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolNullableTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/DateTimeOffSetTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/EnumTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/ToStringTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAdo/ShenTongAdoTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAopTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongCodeFirstTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongDbFirstTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/ConvertTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/DateTimeTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/MathTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/OtherTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/StringTest.cs create mode 100644 FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/TimeSpanTest.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/Curd/OnConflictDoUpdate.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/Curd/ShenTongDelete.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsert.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsertOrUpdate.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/Curd/ShenTongSelect.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/Curd/ShenTongUpdate.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongAdo.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongConnectionPool.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongCodeFirst.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongDbFirst.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongExpression.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongExtensions.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongProvider.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs create mode 100644 Providers/FreeSql.Provider.ShenTong/key.snk create mode 100644 Providers/FreeSql.Provider.ShenTong/lib/Mono.Security.dll create mode 100644 Providers/FreeSql.Provider.ShenTong/lib/System.Data.OscarClient.dll diff --git a/FreeSql.DbContext/DbSet/DbSet.cs b/FreeSql.DbContext/DbSet/DbSet.cs index 4180e750..a86991d8 100644 --- a/FreeSql.DbContext/DbSet/DbSet.cs +++ b/FreeSql.DbContext/DbSet/DbSet.cs @@ -277,6 +277,7 @@ namespace FreeSql case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.OdbcKingbaseES: + case DataType.ShenTong: return true; default: if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 52626a89..a88e711e 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -42,6 +42,7 @@ namespace FreeSql case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.OdbcKingbaseES: + case DataType.ShenTong: if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { await DbContextFlushCommandAsync(); @@ -104,6 +105,7 @@ namespace FreeSql case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.OdbcKingbaseES: + case DataType.ShenTong: await DbContextFlushCommandAsync(); var rets = await this.OrmInsert(data).ExecuteInsertedAsync(); if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_db.OrmOriginal.Ado.DataType} 的返回数据,与添加的数目不匹配"); diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 743dfeef..343ea58b 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -41,6 +41,7 @@ namespace FreeSql case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.OdbcKingbaseES: + case DataType.ShenTong: if (_tableIdentitys.Length == 1) { DbContextFlushCommand(); @@ -107,6 +108,7 @@ namespace FreeSql case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.OdbcKingbaseES: + case DataType.ShenTong: DbContextFlushCommand(); var rets = this.OrmInsert(data).ExecuteInserted(); if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_db.OrmOriginal.Ado.DataType} 的返回数据,与添加的数目不匹配"); diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 4854f49c..132d875e 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -125,13 +125,6 @@ 清空状态数据 - - - 根据 lambda 条件删除数据 - - - - 添加 @@ -486,14 +479,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj index 1748585d..96f5e946 100644 --- a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj +++ b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj @@ -39,6 +39,7 @@ + @@ -47,6 +48,12 @@ ..\..\Providers\FreeSql.Provider.Dameng\lib\DmProvider\netstandard2.0\DmProvider.dll + + ..\..\Providers\FreeSql.Provider.ShenTong\lib\System.Data.OscarClient.dll + + + ..\..\Providers\FreeSql.Provider.ShenTong\lib\Mono.Security.dll + diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongDeleteTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongDeleteTest.cs new file mode 100644 index 00000000..6f07533b --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongDeleteTest.cs @@ -0,0 +1,105 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongDeleteTest + { + + IDelete delete => g.shentong.Delete(); + + [Table(Name = "tb_topic_del")] + class Topic + { + [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 Dywhere() + { + Assert.Null(g.shentong.Delete().ToSql()); + var sql = g.shentong.Delete(new[] { 1, 2 }).ToSql(); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" = 1 OR \"ID\" = 2)", sql); + + sql = g.shentong.Delete(new Topic { Id = 1, Title = "test" }).ToSql(); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" = 1)", sql); + + sql = g.shentong.Delete(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).ToSql(); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" = 1 OR \"ID\" = 2)", sql); + + sql = g.shentong.Delete(new { id = 1 }).ToSql(); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" = 1)", sql); + + sql = g.shentong.Delete(new[] { new { Id1 = 1, Id2 = 10 }, new { Id1 = 2, Id2 = 20 } }).ToSql(); + Assert.Equal("DELETE FROM \"MULTIPKTOPIC\" WHERE (\"ID1\" = 1 AND \"ID2\" = 10 OR \"ID1\" = 2 AND \"ID2\" = 20)", sql); + } + class MultiPkTopic + { + [Column(IsPrimary = true)] + public int Id1 { get; set; } + [Column(IsPrimary = true)] + public int Id2 { get; set; } + public int Clicks { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + + [Fact] + public void Where() + { + var sql = delete.Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" = 1)", sql); + + sql = delete.Where("id = @id", new { id = 1 }).ToSql().Replace("\r\n", ""); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (id = @id)", sql); + + var item = new Topic { Id = 1, Title = "newtitle" }; + sql = delete.Where(item).ToSql().Replace("\r\n", ""); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" = 1)", sql); + + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + sql = delete.Where(items).ToSql().Replace("\r\n", ""); + Assert.Equal("DELETE FROM \"TB_TOPIC_DEL\" WHERE (\"ID\" IN (1,2,3,4,5,6,7,8,9,10))", sql); + } + [Fact] + public void ExecuteAffrows() + { + + var id = g.shentong.Insert(new Topic { Title = "xxxx" }).ExecuteIdentity(); + Assert.Equal(1, delete.Where(a => a.Id == id).ExecuteAffrows()); + } + [Fact] + public void ExecuteDeleted() + { + + delete.Where(a => a.Id > 0).ExecuteDeleted(); + } + + [Fact] + public void AsTable() + { + Assert.Null(g.shentong.Delete().ToSql()); + var sql = g.shentong.Delete(new[] { 1, 2 }).AsTable(a => "TopicAsTable").ToSql(); + Assert.Equal("DELETE FROM \"TOPICASTABLE\" WHERE (\"ID\" = 1 OR \"ID\" = 2)", sql); + + sql = g.shentong.Delete(new Topic { Id = 1, Title = "test" }).AsTable(a => "TopicAsTable").ToSql(); + Assert.Equal("DELETE FROM \"TOPICASTABLE\" WHERE (\"ID\" = 1)", sql); + + sql = g.shentong.Delete(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).AsTable(a => "TopicAsTable").ToSql(); + Assert.Equal("DELETE FROM \"TOPICASTABLE\" WHERE (\"ID\" = 1 OR \"ID\" = 2)", sql); + + sql = g.shentong.Delete(new { id = 1 }).AsTable(a => "TopicAsTable").ToSql(); + Assert.Equal("DELETE FROM \"TOPICASTABLE\" WHERE (\"ID\" = 1)", sql); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertOrUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertOrUpdateTest.cs new file mode 100644 index 00000000..4efd6731 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertOrUpdateTest.cs @@ -0,0 +1,323 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongInsertOrUpdateTestpublic + { + IFreeSql fsql => g.shentong; + + [Fact] + public void InsertOrUpdate_OnlyPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 1 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou01 { id = 2 }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 2 as ID ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID +UNION ALL + SELECT 2 +UNION ALL + SELECT 3 +UNION ALL + SELECT 4 ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(2, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou01 { id = 1 }, new tbiou01 { id = 2 }, new tbiou01 { id = 3 }, new tbiou01 { id = 4 } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU01"" t1 +USING (SELECT 1 as ID +UNION ALL + SELECT 2 +UNION ALL + SELECT 3 +UNION ALL + SELECT 4 ) t2 ON (t1.""ID"" = t2.ID) +WHEN NOT MATCHED THEN + insert (""ID"") + values (t2.ID)", sql); + Assert.Equal(0, iou.ExecuteAffrows()); + } + class tbiou01 + { + public int id { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '011' as NAME ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou02 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 2 as ID, '02' as NAME ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "01" }, new tbiou02 { id = 2, name = "02" }, new tbiou02 { id = 3, name = "03" }, new tbiou02 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '01' as NAME +UNION ALL + SELECT 2, '02' +UNION ALL + SELECT 3, '03' +UNION ALL + SELECT 4, '04' ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou02 { id = 1, name = "001" }, new tbiou02 { id = 2, name = "002" }, new tbiou02 { id = 3, name = "003" }, new tbiou02 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU02"" t1 +USING (SELECT 1 as ID, '001' as NAME +UNION ALL + SELECT 2, '002' +UNION ALL + SELECT 3, '003' +UNION ALL + SELECT 4, '004' ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"") + values (t2.ID, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou02 + { + public int id { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_TwoPrimary() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "01", name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 1, id2 = "02", name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '02' as ID2, '011' as NAME ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou03 { id1 = 2, id2 = "02", name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 2 as ID1, '02' as ID2, '02' as NAME ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "01" }, new tbiou03 { id1 = 2, id2 = "02", name = "02" }, new tbiou03 { id1 = 3, id2 = "03", name = "03" }, new tbiou03 { id1 = 4, id2 = "04", name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '01' as NAME +UNION ALL + SELECT 2, '02', '02' +UNION ALL + SELECT 3, '03', '03' +UNION ALL + SELECT 4, '04', '04' ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou03 { id1 = 1, id2 = "01", name = "001" }, new tbiou03 { id1 = 2, id2 = "02", name = "002" }, new tbiou03 { id1 = 3, id2 = "03", name = "003" }, new tbiou03 { id1 = 4, id2 = "04", name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU03"" t1 +USING (SELECT 1 as ID1, '01' as ID2, '001' as NAME +UNION ALL + SELECT 2, '02', '002' +UNION ALL + SELECT 3, '03', '003' +UNION ALL + SELECT 4, '04', '004' ) t2 ON (t1.""ID1"" = t2.ID1 AND t1.""ID2"" = t2.ID2) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME +WHEN NOT MATCHED THEN + insert (""ID1"", ""ID2"", ""NAME"") + values (t2.ID1, t2.ID2, t2.NAME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => a.id1 == 1 && a.id2 == "01" || a.id1 == 2 && a.id2 == "02" || a.id1 == 3 && a.id2 == "03" || a.id1 == 4 && a.id2 == "04").ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id1).Count()); + } + class tbiou03 + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public string id2 { get; set; } + public string name { get; set; } + } + + [Fact] + public void InsertOrUpdate_OnePrimaryAndVersionAndCanUpdate() + { + fsql.Delete().Where("1=1").ExecuteAffrows(); + var iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "01" }); + var sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, current_timestamp as CREATETIME ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 1, name = "011" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '011' as NAME, 0 as VERSION, current_timestamp as CREATETIME ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new tbiou04 { id = 2, name = "02" }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 2 as ID, '02' as NAME, 0 as VERSION, current_timestamp as CREATETIME ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(1, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "01" }, new tbiou04 { id = 2, name = "02" }, new tbiou04 { id = 3, name = "03" }, new tbiou04 { id = 4, name = "04" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '01' as NAME, 0 as VERSION, current_timestamp as CREATETIME +UNION ALL + SELECT 2, '02', 0, current_timestamp +UNION ALL + SELECT 3, '03', 0, current_timestamp +UNION ALL + SELECT 4, '04', 0, current_timestamp ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + + iou = fsql.InsertOrUpdate().SetSource(new[] { new tbiou04 { id = 1, name = "001" }, new tbiou04 { id = 2, name = "002" }, new tbiou04 { id = 3, name = "003" }, new tbiou04 { id = 4, name = "004" } }); + sql = iou.ToSql(); + Assert.Equal(@"MERGE INTO ""TBIOU04"" t1 +USING (SELECT 1 as ID, '001' as NAME, 0 as VERSION, current_timestamp as CREATETIME +UNION ALL + SELECT 2, '002', 0, current_timestamp +UNION ALL + SELECT 3, '003', 0, current_timestamp +UNION ALL + SELECT 4, '004', 0, current_timestamp ) t2 ON (t1.""ID"" = t2.ID) +WHEN MATCHED THEN + update set ""NAME"" = t2.NAME, ""VERSION"" = t1.""VERSION"" + 1 +WHEN NOT MATCHED THEN + insert (""ID"", ""NAME"", ""VERSION"", ""CREATETIME"") + values (t2.ID, t2.NAME, t2.VERSION, t2.CREATETIME)", sql); + Assert.Equal(4, iou.ExecuteAffrows()); + var lst = fsql.Select().Where(a => new[] { 1, 2, 3, 4 }.Contains(a.id)).ToList(); + Assert.Equal(4, lst.Where(a => a.name == "00" + a.id).Count()); + } + class tbiou04 + { + public int id { get; set; } + public string name { get; set; } + [Column(IsVersion = true)] + public int version { get; set; } + [Column(CanUpdate = false, ServerTime = DateTimeKind.Local)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertTest.cs new file mode 100644 index 00000000..d036d099 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongInsertTest.cs @@ -0,0 +1,141 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongInsertTest + { + + IInsert insert => g.shentong.Insert(); + + [Table(Name = "TB_TOPIC_INSERT")] + class Topic + { + [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 AppendData() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + var sql = insert.AppendData(items.First()).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"CLICKS\", \"TITLE\", \"CREATETIME\") VALUES(@Clicks_0, @Title_0, @CreateTime_0)", sql); + + sql = insert.AppendData(items).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"CLICKS\", \"TITLE\", \"CREATETIME\") VALUES(@Clicks_0, @Title_0, @CreateTime_0), (@Clicks_1, @Title_1, @CreateTime_1), (@Clicks_2, @Title_2, @CreateTime_2), (@Clicks_3, @Title_3, @CreateTime_3), (@Clicks_4, @Title_4, @CreateTime_4), (@Clicks_5, @Title_5, @CreateTime_5), (@Clicks_6, @Title_6, @CreateTime_6), (@Clicks_7, @Title_7, @CreateTime_7), (@Clicks_8, @Title_8, @CreateTime_8), (@Clicks_9, @Title_9, @CreateTime_9)", sql); + + sql = insert.AppendData(items).InsertColumns(a => a.Title).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"TITLE\") VALUES(@Title_0), (@Title_1), (@Title_2), (@Title_3), (@Title_4), (@Title_5), (@Title_6), (@Title_7), (@Title_8), (@Title_9)", sql); + + sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"CLICKS\", \"TITLE\") VALUES(@Clicks_0, @Title_0), (@Clicks_1, @Title_1), (@Clicks_2, @Title_2), (@Clicks_3, @Title_3), (@Clicks_4, @Title_4), (@Clicks_5, @Title_5), (@Clicks_6, @Title_6), (@Clicks_7, @Title_7), (@Clicks_8, @Title_8), (@Clicks_9, @Title_9)", sql); + } + + [Fact] + public void InsertColumns() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + var sql = insert.AppendData(items).InsertColumns(a => a.Title).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"TITLE\") VALUES(@Title_0), (@Title_1), (@Title_2), (@Title_3), (@Title_4), (@Title_5), (@Title_6), (@Title_7), (@Title_8), (@Title_9)", sql); + + sql = insert.AppendData(items).InsertColumns(a => new { a.Title, a.Clicks }).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"CLICKS\", \"TITLE\") VALUES(@Clicks_0, @Title_0), (@Clicks_1, @Title_1), (@Clicks_2, @Title_2), (@Clicks_3, @Title_3), (@Clicks_4, @Title_4), (@Clicks_5, @Title_5), (@Clicks_6, @Title_6), (@Clicks_7, @Title_7), (@Clicks_8, @Title_8), (@Clicks_9, @Title_9)", sql); + } + [Fact] + public void IgnoreColumns() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + var sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"CLICKS\", \"TITLE\") VALUES(@Clicks_0, @Title_0), (@Clicks_1, @Title_1), (@Clicks_2, @Title_2), (@Clicks_3, @Title_3), (@Clicks_4, @Title_4), (@Clicks_5, @Title_5), (@Clicks_6, @Title_6), (@Clicks_7, @Title_7), (@Clicks_8, @Title_8), (@Clicks_9, @Title_9)", sql); + + sql = insert.AppendData(items).IgnoreColumns(a => new { a.Title, a.CreateTime }).ToSql(); + Assert.Equal("INSERT INTO \"TB_TOPIC_INSERT\"(\"CLICKS\") VALUES(@Clicks_0), (@Clicks_1), (@Clicks_2), (@Clicks_3), (@Clicks_4), (@Clicks_5), (@Clicks_6), (@Clicks_7), (@Clicks_8), (@Clicks_9)", sql); + + g.shentong.Delete().Where("1=1").ExecuteAffrows(); + var itemsIgnore = new List(); + for (var a = 0; a < 2072; a++) itemsIgnore.Add(new TopicIgnore { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100, CreateTime = DateTime.Now }); + g.shentong.Insert().AppendData(itemsIgnore).IgnoreColumns(a => new { a.Title }).ExecuteAffrows(); + Assert.Equal(2072, itemsIgnore.Count); + Assert.Equal(2072, g.shentong.Select().Where(a => a.Title == null).Count()); + } + [Table(Name = "TB_TOPICIGNORECOLUMNS")] + class TopicIgnore + { + [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 ExecuteAffrows() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + Assert.Equal(1, insert.AppendData(items.First()).ExecuteAffrows()); + Assert.Equal(10, insert.AppendData(items).ExecuteAffrows()); + } + [Fact] + public void ExecuteIdentity() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + Assert.NotEqual(0, insert.AppendData(items.First()).ExecuteIdentity()); + } + [Fact] + public void ExecuteInserted() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + insert.AppendData(items.First()).ExecuteInserted(); + } + + [Fact] + public void AsTable() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newTitle{a}", Clicks = a * 100 }); + + var sql = insert.AppendData(items.First()).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"CLICKS\", \"TITLE\", \"CREATETIME\") VALUES(@Clicks_0, @Title_0, @CreateTime_0)", sql); + + sql = insert.AppendData(items).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"CLICKS\", \"TITLE\", \"CREATETIME\") VALUES(@Clicks_0, @Title_0, @CreateTime_0), (@Clicks_1, @Title_1, @CreateTime_1), (@Clicks_2, @Title_2, @CreateTime_2), (@Clicks_3, @Title_3, @CreateTime_3), (@Clicks_4, @Title_4, @CreateTime_4), (@Clicks_5, @Title_5, @CreateTime_5), (@Clicks_6, @Title_6, @CreateTime_6), (@Clicks_7, @Title_7, @CreateTime_7), (@Clicks_8, @Title_8, @CreateTime_8), (@Clicks_9, @Title_9, @CreateTime_9)", sql); + + sql = insert.AppendData(items).InsertColumns(a => a.Title).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"TITLE\") VALUES(@Title_0), (@Title_1), (@Title_2), (@Title_3), (@Title_4), (@Title_5), (@Title_6), (@Title_7), (@Title_8), (@Title_9)", sql); + + sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"CLICKS\", \"TITLE\") VALUES(@Clicks_0, @Title_0), (@Clicks_1, @Title_1), (@Clicks_2, @Title_2), (@Clicks_3, @Title_3), (@Clicks_4, @Title_4), (@Clicks_5, @Title_5), (@Clicks_6, @Title_6), (@Clicks_7, @Title_7), (@Clicks_8, @Title_8), (@Clicks_9, @Title_9)", sql); + + sql = insert.AppendData(items).InsertColumns(a => a.Title).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"TITLE\") VALUES(@Title_0), (@Title_1), (@Title_2), (@Title_3), (@Title_4), (@Title_5), (@Title_6), (@Title_7), (@Title_8), (@Title_9)", sql); + + sql = insert.AppendData(items).InsertColumns(a => new { a.Title, a.Clicks }).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"CLICKS\", \"TITLE\") VALUES(@Clicks_0, @Title_0), (@Clicks_1, @Title_1), (@Clicks_2, @Title_2), (@Clicks_3, @Title_3), (@Clicks_4, @Title_4), (@Clicks_5, @Title_5), (@Clicks_6, @Title_6), (@Clicks_7, @Title_7), (@Clicks_8, @Title_8), (@Clicks_9, @Title_9)", sql); + + sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"CLICKS\", \"TITLE\") VALUES(@Clicks_0, @Title_0), (@Clicks_1, @Title_1), (@Clicks_2, @Title_2), (@Clicks_3, @Title_3), (@Clicks_4, @Title_4), (@Clicks_5, @Title_5), (@Clicks_6, @Title_6), (@Clicks_7, @Title_7), (@Clicks_8, @Title_8), (@Clicks_9, @Title_9)", sql); + + sql = insert.AppendData(items).IgnoreColumns(a => new { a.Title, a.CreateTime }).AsTable(a => "Topic_InsertAsTable").ToSql(); + Assert.Equal("INSERT INTO \"TOPIC_INSERTASTABLE\"(\"CLICKS\") VALUES(@Clicks_0), (@Clicks_1), (@Clicks_2), (@Clicks_3), (@Clicks_4), (@Clicks_5), (@Clicks_6), (@Clicks_7), (@Clicks_8), (@Clicks_9)", sql); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongSelectTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongSelectTest.cs new file mode 100644 index 00000000..57b20641 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongSelectTest.cs @@ -0,0 +1,1863 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongSelectTest + { + + ISelect select => g.shentong.Select(); + + [Table(Name = "tb_topic")] + class Topic + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public int TypeGuid { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + class TestTypeInfo + { + [Column(IsIdentity = true)] + public int Guid { get; set; } + public int ParentId { get; set; } + public TestTypeParentInfo Parent { get; set; } + public string Name { get; set; } + } + class TestTypeParentInfo + { + public int Id { get; set; } + public string Name { get; set; } + + public List Types { get; set; } + } + public partial class Song + { + [Column(IsIdentity = true)] + public int Id { get; set; } + public DateTime? Create_time { get; set; } + public bool? Is_deleted { get; set; } + public string Title { get; set; } + public string Url { get; set; } + + public virtual ICollection Tags { get; set; } + } + public partial class Song_tag + { + public int Song_id { get; set; } + public virtual Song Song { get; set; } + + public int Tag_id { get; set; } + public virtual Tag Tag { get; set; } + } + public partial class Tag + { + [Column(IsIdentity = true)] + public int Id { get; set; } + public int? Parent_id { get; set; } + public virtual Tag Parent { get; set; } + + public decimal? Ddd { get; set; } + public string Name { get; set; } + + public virtual ICollection Songs { get; set; } + public virtual ICollection Tags { get; set; } + } + + [Fact] + public void AsSelect() + { + //OneToOne、ManyToOne + var t0 = g.shentong.Select().Where(a => a.Parent.Parent.Name == "粤语").ToSql(); + //SELECT a.`Id`, a.`Parent_id`, a__Parent.`Id` as3, a__Parent.`Parent_id` as4, a__Parent.`Ddd`, a__Parent.`Name`, a.`Ddd` as7, a.`Name` as8 + //FROM `Tag` a + //LEFT JOIN `Tag` a__Parent ON a__Parent.`Id` = a.`Parent_id` + //LEFT JOIN `Tag` a__Parent__Parent ON a__Parent__Parent.`Id` = a__Parent.`Parent_id` + //WHERE (a__Parent__Parent.`Name` = '粤语') + + //OneToMany + var t1 = g.shentong.Select().Where(a => a.Tags.AsSelect().Any(t => t.Parent.Id == 10)).ToSql(); + //SELECT a.`Id`, a.`Parent_id`, a.`Ddd`, a.`Name` + //FROM `Tag` a + //WHERE (exists(SELECT 1 + // FROM `Tag` t + // LEFT JOIN `Tag` t__Parent ON t__Parent.`Id` = t.`Parent_id` + // WHERE (t__Parent.`Id` = 10) AND (t.`Parent_id` = a.`Id`) + // limit 0,1)) + + //ManyToMany + var t2 = g.shentong.Select().Where(s => s.Tags.AsSelect().Any(t => t.Name == "国语")).ToSql(); + //SELECT a.`Id`, a.`Create_time`, a.`Is_deleted`, a.`Title`, a.`Url` + //FROM `Song` a + //WHERE(exists(SELECT 1 + // FROM `Song_tag` Mt_Ms + // WHERE(Mt_Ms.`Song_id` = a.`Id`) AND(exists(SELECT 1 + // FROM `Tag` t + // WHERE(t.`Name` = '国语') AND(t.`Id` = Mt_Ms.`Tag_id`) + // limit 0, 1)) + // limit 0, 1)) + } + + [Fact] + public void Lazy() + { + var tags = g.shentong.Select().Where(a => a.Parent.Name == "xxx") + .LeftJoin(a => a.Parent_id == a.Parent.Id) + .ToSql(); + + var songs = g.shentong.Select().Limit(10).ToList(); + } + + [Fact] + public void ToDataTable() + { + 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 }); + + Assert.Single(g.shentong.Insert().AppendData(items.First()).ExecuteInserted()); + Assert.Equal(10, g.shentong.Insert().AppendData(items).ExecuteInserted().Count); + + //items = Enumerable.Range(0, 9989).Select(a => new Topic { Title = "newtitle" + a, CreateTime = DateTime.Now }).ToList(); + //Assert.Equal(9989, g.shentong.Insert(items).ExecuteAffrows()); + + var dt1 = select.Limit(10).ToDataTable(); + var dt2 = select.Limit(10).ToDataTable("id, 222"); + var dt3 = select.Limit(10).ToDataTable(a => new { id = a.Id, name2 = a.Type.Name, now = DateTime.Now }); + } + class TestDto + { + public int id { get; set; } + public string name { get; set; } //这是join表的属性 + public int ParentId { get; set; } //这是join表的属性 + } + class TestDto2 + { + public int id { get; set; } + public string name { get; set; } //这是join表的属性 + public int ParentId { get; set; } //这是join表的属性 + + public TestDto2() { } + public TestDto2(int id, string name) + { + this.id = id; + this.name = name; + } + } + [Fact] + public void ToList() + { + + var testDto1 = select.Limit(10).ToList(a => new TestDto { id = a.Id, name = a.Title }); + var testDto2 = select.Limit(10).ToList(a => new TestDto()); + var testDto3 = select.Limit(10).ToList(a => new TestDto { }); + var testDto4 = select.Limit(10).ToList(a => new TestDto() { }); + + var testDto11 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto { id = a.Id, name = a.Title }); + var testDto22 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto()); + var testDto33 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto { }); + var testDto44 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto() { }); + + var testDto211 = select.Limit(10).ToList(a => new TestDto2(a.Id, a.Title)); + var testDto212 = select.Limit(10).ToList(a => new TestDto2()); + var testDto213 = select.Limit(10).ToList(a => new TestDto2 { }); + var testDto214 = select.Limit(10).ToList(a => new TestDto2() { }); + var testDto215 = select.Limit(10).ToList(); + + var testDto2211 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto2(a.Id, a.Title)); + var testDto2222 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto2()); + var testDto2233 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto2 { }); + var testDto2244 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(a => new TestDto2() { }); + var testDto2255 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).Limit(10).ToList(); + + var t1 = g.shentong.Select().Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql(); + var t2 = g.shentong.Select().As("b").Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToSql(); + + + var sql1 = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).ToSql(); + var sql2 = select.LeftJoin((a, b) => a.TypeGuid == b.Guid && b.Name == "111").ToSql(); + var sql3 = select.LeftJoin("TestTypeInfo b on b.Guid = a.TypeGuid").ToSql(); + + //g.shentong.Select().Join((a, b, c) => new Model.JoinResult3( + // Model.JoinType.LeftJoin, a.TypeGuid == b.Guid, + // Model.JoinType.InnerJoin, c.Id == b.ParentId && c.Name == "xxx") + //); + + //var sql4 = select.From((a, b, c) => new SelectFrom() + // .InnerJoin(a.TypeGuid == b.Guid) + // .LeftJoin(c.Id == b.ParentId) + // .Where(b.Name == "xxx")) + //.Where(a => a.Id == 1).ToSql(); + + var sql4 = select.From((s, b, c) => s + .InnerJoin(a => a.TypeGuid == b.Guid) + .LeftJoin(a => c.Id == b.ParentId) + .Where(a => b.Name == "xxx")).ToSql(); + //.Where(a => a.Id == 1).ToSql(); + + + var list111 = select.From((s, b, c) => s + .InnerJoin(a => a.TypeGuid == b.Guid) + .LeftJoin(a => c.Id == b.ParentId) + .Where(a => b.Name != "xxx")); + var list111sql = list111.ToSql(); + var list111data = list111.ToList((a, b, c) => new + { + a.Id, + title_substring = a.Title.Substring(0, 1), + a.Type, + ccc = new { a.Id, a.Title }, + tp = a.Type, + tp2 = new + { + a.Id, + tp2 = a.Type.Name + }, + tp3 = new + { + a.Id, + tp33 = new + { + a.Id + } + } + }); + + var ttt122 = g.shentong.Select().Where(a => a.Id > 0).ToSql(); + var sql5 = g.shentong.Select().From((s, b, c) => s).Where((a, b, c) => a.Id == b.ParentId).ToSql(); + var t11112 = g.shentong.Select().ToList(a => new + { + a.Id, + a.Title, + a.Type, + ccc = new { a.Id, a.Title }, + tp = a.Type, + tp2 = new + { + a.Id, + tp2 = a.Type.Name + }, + tp3 = new + { + a.Id, + tp33 = new + { + a.Id + } + } + + }); + + var t100 = g.shentong.Select().Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToList(); + var t101 = g.shentong.Select().As("b").Where("").Where(a => a.Id > 0).Skip(100).Limit(200).ToList(); + + + var t1111 = g.shentong.Select().ToList(a => new { a.Id, a.Title, a.Type }); + + var t2222 = g.shentong.Select().ToList(a => new { a.Id, a.Title, a.Type.Name }); + + g.shentong.Insert().AppendData(new TestGuidIdToList()).ExecuteAffrows(); + var testGuidId5 = g.shentong.Select().ToList(); + var testGuidId6 = g.shentong.Select().ToList(a => a.id); + + var t11 = select.Where(a => a.Type.Name.Length > 0).ToList(true); + var t21 = select.Where(a => a.Type.Parent.Name.Length > 0).ToList(true); + + g.shentong.Delete().Where("1=1").ExecuteAffrows(); + var repo = g.shentong.GetRepository(); + repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; + repo.Insert(new District + { + Code = "001", + Name = "001_name", + Childs = new List(new[] { + new District{ + Code = "001_01", + Name = "001_01_name" + }, + new District{ + Code = "001_02", + Name = "001_02_name" + } + }) + }); + var ddd = g.shentong.Select().LeftJoin(d => d.ParentCode == d.Parent.Code).ToTreeList(); + Assert.Single(ddd); + Assert.Equal(2, ddd[0].Childs.Count); + } + public class District + { + [Column(IsPrimary = true, StringLength = 6)] + public string Code { get; set; } + + [Column(StringLength = 20, IsNullable = false)] + public string Name { get; set; } + + [Column(StringLength = 6)] + public string ParentCode { get; set; } + + [Navigate(nameof(ParentCode))] + public District Parent { get; set; } + + [Navigate(nameof(ParentCode))] + public List Childs { get; set; } + } + [Fact] + public void ToDictionary() + { + var testDto1 = select.Limit(10).ToDictionary(a => a.Id); + var testDto2 = select.Limit(10).ToDictionary(a => a.Id, a => new { a.Id, a.Title }); + + var repo = g.shentong.GetRepository(); + var dic = repo.Select.Limit(10).ToDictionary(a => a.Id); + var first = dic.First().Value; + first.Clicks++; + repo.Update(first); + } + class TestGuidIdToList + { + public Guid id { get; set; } + public string title { get; set; } = Guid.NewGuid().ToString(); + } + [Fact] + public void ToOne() + { + var testnotfind = select.Where("1=2").First(a => a.CreateTime); + Assert.Equal(default(DateTime), testnotfind); + } + [Fact] + public void ToSql() + { + } + [Fact] + public void Any() + { + var count = select.Where(a => 1 == 1).Count(); + Assert.False(select.Where(a => 1 == 2).Any()); + Assert.Equal(count > 0, select.Where(a => 1 == 1).Any()); + + var sql2222 = select.Where(a => + select.Where(b => b.Id == a.Id && + select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) + //.Offset(a.Id) + .Any() + ).Any(c => c.Id == a.Id + 10) + ); + var sql2222Tolist = sql2222.ToList(); + + var collectionSelect = select.Where(a => + a.Type.Guid == a.TypeGuid && + a.Type.Parent.Id == a.Type.ParentId && + a.Type.Parent.Types.AsSelect().Where(b => b.Name == a.Title).Any(b => b.ParentId == a.Type.Parent.Id) + ); + collectionSelect.ToList(); + } + [Fact] + public void Count() + { + var count = select.Where(a => 1 == 1).Count(); + select.Where(a => 1 == 1).Count(out var count2); + Assert.Equal(count, count2); + Assert.Equal(0, select.Where(a => 1 == 2).Count()); + + var subquery = select.ToSql(a => new + { + all = a, + count = select.Where(b => b.Id > 0 && b.Id == a.Id).Count() + }); + var subqueryList = select.ToList(a => new + { + all = a, + count = select.Where(b => b.Id > 0 && b.Id == a.Id).Count() + }); + } + [Fact] + public void Master() + { + Assert.StartsWith(" SELECT", select.Master().Where(a => 1 == 1).ToSql()); + } + + [Fact] + public void From() + { + + var query2 = select.From((s, b) => s + .LeftJoin(a => a.TypeGuid == b.Guid) + ); + var sql2 = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b ON a.\"TYPEGUID\" = b.\"GUID\"", sql2); + query2.ToList(); + + var query3 = select.From((s, b, c) => s + .LeftJoin(a => a.TypeGuid == b.Guid) + .LeftJoin(a => b.ParentId == c.Id) + ); + var sql3 = query3.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b ON a.\"TYPEGUID\" = b.\"GUID\" LEFT JOIN \"TESTTYPEPARENTINFO\" c ON b.\"PARENTID\" = c.\"ID\"", sql3); + query3.ToList(); + } + [Fact] + public void LeftJoin() + { + //����е�������a.Type��a.Type.Parent ���ǵ������� + var query = select.LeftJoin(a => a.Type.Guid == a.TypeGuid); + var sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.LeftJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx'", sql); + query.ToList(); + + query = select.LeftJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + query.ToList(); + + //���û�е������� + query = select.LeftJoin((a, b) => b.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b ON b.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.LeftJoin((a, b) => b.Guid == a.TypeGuid && b.Name == "xxx"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b ON b.\"GUID\" = a.\"TYPEGUID\" AND b.\"NAME\" = 'xxx'", sql); + query.ToList(); + + query = select.LeftJoin((a, a__Type) => a__Type.Guid == a.TypeGuid && a__Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + query.ToList(); + + //������� + query = select + .LeftJoin(a => a.Type.Guid == a.TypeGuid) + .LeftJoin(a => a.Type.Parent.Id == a.Type.ParentId); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\"", sql); + query.ToList(); + + query = select + .LeftJoin((a, a__Type) => a__Type.Guid == a.TypeGuid) + .LeftJoin((a, c) => c.Id == a.Type.ParentId); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" LEFT JOIN \"TESTTYPEPARENTINFO\" c ON c.\"ID\" = a__Type.\"PARENTID\"", sql); + query.ToList(); + + //���û�е�������b��c������ϵ + var query2 = select.From((s, b, c) => s + .LeftJoin(a => a.TypeGuid == b.Guid) + .LeftJoin(a => b.ParentId == c.Id)); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b ON a.\"TYPEGUID\" = b.\"GUID\" LEFT JOIN \"TESTTYPEPARENTINFO\" c ON b.\"PARENTID\" = c.\"ID\"", sql); + query2.ToList(); + + //������϶����㲻�� + query = select.LeftJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\""); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.LeftJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", new { bname = "xxx" }); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", sql); + query.ToList(); + } + [Fact] + public void InnerJoin() + { + //����е�������a.Type��a.Type.Parent ���ǵ������� + var query = select.InnerJoin(a => a.Type.Guid == a.TypeGuid); + var sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.InnerJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx'", sql); + query.ToList(); + + query = select.InnerJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + query.ToList(); + + //���û�е������� + query = select.InnerJoin((a, b) => b.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" b ON b.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.InnerJoin((a, b) => b.Guid == a.TypeGuid && b.Name == "xxx"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" b ON b.\"GUID\" = a.\"TYPEGUID\" AND b.\"NAME\" = 'xxx'", sql); + query.ToList(); + + query = select.InnerJoin((a, a__Type) => a__Type.Guid == a.TypeGuid && a__Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + query.ToList(); + + //������� + query = select + .InnerJoin(a => a.Type.Guid == a.TypeGuid) + .InnerJoin(a => a.Type.Parent.Id == a.Type.ParentId); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" INNER JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\"", sql); + query.ToList(); + + query = select + .InnerJoin((a, a__Type) => a__Type.Guid == a.TypeGuid) + .InnerJoin((a, c) => c.Id == a.Type.ParentId); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" INNER JOIN \"TESTTYPEPARENTINFO\" c ON c.\"ID\" = a__Type.\"PARENTID\"", sql); + query.ToList(); + + //���û�е�������b��c������ϵ + var query2 = select.From((s, b, c) => s + .InnerJoin(a => a.TypeGuid == b.Guid) + .InnerJoin(a => b.ParentId == c.Id)); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" b ON a.\"TYPEGUID\" = b.\"GUID\" INNER JOIN \"TESTTYPEPARENTINFO\" c ON b.\"PARENTID\" = c.\"ID\"", sql); + query2.ToList(); + + //������϶����㲻�� + query = select.InnerJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\""); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.InnerJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", new { bname = "xxx" }); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a INNER JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", sql); + query.ToList(); + + } + [Fact] + public void RightJoin() + { + //����е�������a.Type��a.Type.Parent ���ǵ������� + var query = select.RightJoin(a => a.Type.Guid == a.TypeGuid); + var sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.RightJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx'", sql); + query.ToList(); + + query = select.RightJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + query.ToList(); + + //���û�е������� + query = select.RightJoin((a, b) => b.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" b ON b.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.RightJoin((a, b) => b.Guid == a.TypeGuid && b.Name == "xxx"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" b ON b.\"GUID\" = a.\"TYPEGUID\" AND b.\"NAME\" = 'xxx'", sql); + query.ToList(); + + query = select.RightJoin((a, a__Type) => a__Type.Guid == a.TypeGuid && a__Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + query.ToList(); + + //������� + query = select + .RightJoin(a => a.Type.Guid == a.TypeGuid) + .RightJoin(a => a.Type.Parent.Id == a.Type.ParentId); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" RIGHT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\"", sql); + query.ToList(); + + query = select + .RightJoin((a, a__Type) => a__Type.Guid == a.TypeGuid) + .RightJoin((a, c) => c.Id == a.Type.ParentId); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" RIGHT JOIN \"TESTTYPEPARENTINFO\" c ON c.\"ID\" = a__Type.\"PARENTID\"", sql); + query.ToList(); + + //���û�е�������b��c������ϵ + var query2 = select.From((s, b, c) => s + .RightJoin(a => a.TypeGuid == b.Guid) + .RightJoin(a => b.ParentId == c.Id)); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" b ON a.\"TYPEGUID\" = b.\"GUID\" RIGHT JOIN \"TESTTYPEPARENTINFO\" c ON b.\"PARENTID\" = c.\"ID\"", sql); + query2.ToList(); + + //������϶����㲻�� + query = select.RightJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\""); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\"", sql); + query.ToList(); + + query = select.RightJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", new { bname = "xxx" }); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a RIGHT JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", sql); + query.ToList(); + + } + [Fact] + public void Where() + { + var sqltmp1 = select.Where(a => a.Id == 0 && (a.Title == "x" || a.Title == "y") && a.Clicks == 1).ToSql(); + var sqltmp2 = select.Where(a => a.Id.Equals(true) && (a.Title.Equals("x") || a.Title.Equals("y")) && a.Clicks.Equals(1)).ToSql(); + var sqltmp3 = select.Where(a => a.Id == 0).Where(a => ((a.Title == "x" && a.Title == "z") || a.Title == "y")).ToSql(); + + var sqltmp4 = select.Where(a => (a.Id - 10) / 2 > 0).ToSql(); + + //����е�������a.Type��a.Type.Parent ���ǵ������� + var query = select.Where(a => a.Id == 10); + var sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE (a.\"ID\" = 10)", sql); + query.ToList(); + + query = select.Where(a => a.Id == 10 && a.Id > 10 || a.Clicks > 100); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE ((a.\"ID\" = 10 AND a.\"ID\" > 10 OR a.\"CLICKS\" > 100))", sql); + query.ToList(); + + query = select.Where(a => a.Id == 10).Where(a => a.Clicks > 100); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE (a.\"ID\" = 10) AND (a.\"CLICKS\" > 100)", sql); + query.ToList(); + + query = select.Where(a => a.Type.Name == "typeTitle"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" WHERE (a__Type.\"NAME\" = 'typeTitle')", sql); + query.ToList(); + + query = select.Where(a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" WHERE (a__Type.\"NAME\" = 'typeTitle' AND a__Type.\"GUID\" = a.\"TYPEGUID\")", sql); + query.ToList(); + + query = select.Where(a => a.Type.Parent.Name == "tparent"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"NAME\" = 'tparent')", sql); + query.ToList(); + + //���û�е������ԣ��򵥶������ + query = select.Where((a, b) => b.Guid == a.TypeGuid && b.Name == "typeTitle"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a, \"TESTTYPEINFO\" b WHERE (b.\"GUID\" = a.\"TYPEGUID\" AND b.\"NAME\" = 'typeTitle')", sql); + query.ToList(); + + query = select.Where((a, b) => b.Name == "typeTitle" && b.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a, \"TESTTYPEINFO\" b WHERE (b.\"NAME\" = 'typeTitle' AND b.\"GUID\" = a.\"TYPEGUID\")", sql); + query.ToList(); + + query = select.Where((a, b, c) => c.Name == "tparent"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a, \"TESTTYPEPARENTINFO\" c WHERE (c.\"NAME\" = 'tparent')", sql); + query.ToList(); + + //����һ�� From ��Ķ������ + var query2 = select.From((s, b, c) => s + .Where(a => a.Id == 10 && c.Name == "xxx") + .Where(a => b.ParentId == 20)); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a, \"TESTTYPEINFO\" b, \"TESTTYPEPARENTINFO\" c WHERE (a.\"ID\" = 10 AND c.\"NAME\" = 'xxx') AND (b.\"PARENTID\" = 20)", sql); + query2.ToList(); + + //������϶����㲻�� + query = select.Where("a.\"CLICKS\" > 100 and a.\"ID\" = @id", new { id = 10 }); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE (a.\"CLICKS\" > 100 and a.\"ID\" = @id)", sql); + query.ToList(); + } + [Fact] + public void WhereIf() + { + //����е�������a.Type��a.Type.Parent ���ǵ������� + var query = select.WhereIf(true, a => a.Id == 10); + var sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE (a.\"ID\" = 10)", sql); + query.ToList(); + + query = select.WhereIf(true, a => a.Id == 10 && a.Id > 10 || a.Clicks > 100); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE ((a.\"ID\" = 10 AND a.\"ID\" > 10 OR a.\"CLICKS\" > 100))", sql); + query.ToList(); + + query = select.WhereIf(true, a => a.Id == 10).WhereIf(true, a => a.Clicks > 100); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE (a.\"ID\" = 10) AND (a.\"CLICKS\" > 100)", sql); + query.ToList(); + + query = select.WhereIf(true, a => a.Type.Name == "typeTitle"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" WHERE (a__Type.\"NAME\" = 'typeTitle')", sql); + query.ToList(); + + query = select.WhereIf(true, a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" WHERE (a__Type.\"NAME\" = 'typeTitle' AND a__Type.\"GUID\" = a.\"TYPEGUID\")", sql); + query.ToList(); + + query = select.WhereIf(true, a => a.Type.Parent.Name == "tparent"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a LEFT JOIN \"TESTTYPEINFO\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" LEFT JOIN \"TESTTYPEPARENTINFO\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"NAME\" = 'tparent')", sql); + query.ToList(); + + //����һ�� From ��Ķ������ + var query2 = select.From((s, b, c) => s + .WhereIf(true, a => a.Id == 10 && c.Name == "xxx") + .WhereIf(true, a => b.ParentId == 20)); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a, \"TESTTYPEINFO\" b, \"TESTTYPEPARENTINFO\" c WHERE (a.\"ID\" = 10 AND c.\"NAME\" = 'xxx') AND (b.\"PARENTID\" = 20)", sql); + query2.ToList(); + + //������϶����㲻�� + query = select.WhereIf(true, "a.\"CLICKS\" > 100 and a.\"ID\" = @id", new { id = 10 }); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a WHERE (a.\"CLICKS\" > 100 and a.\"ID\" = @id)", sql); + query.ToList(); + + // ==========================================WhereIf(false) + + //����е�������a.Type��a.Type.Parent ���ǵ������� + query = select.WhereIf(false, a => a.Id == 10); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + + query = select.WhereIf(false, a => a.Id == 10 && a.Id > 10 || a.Clicks > 100); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + + query = select.WhereIf(false, a => a.Id == 10).WhereIf(false, a => a.Clicks > 100); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + + query = select.WhereIf(false, a => a.Type.Name == "typeTitle"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + + query = select.WhereIf(false, a => a.Type.Name == "typeTitle" && a.Type.Guid == a.TypeGuid); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + + query = select.WhereIf(false, a => a.Type.Parent.Name == "tparent"); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + + //����һ�� From ��Ķ������ + query2 = select.From((s, b, c) => s + .WhereIf(false, a => a.Id == 10 && c.Name == "xxx") + .WhereIf(false, a => b.ParentId == 20)); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a, \"TESTTYPEINFO\" b, \"TESTTYPEPARENTINFO\" c", sql); + query2.ToList(); + + //������϶����㲻�� + query = select.WhereIf(false, "a.\"CLICKS\" > 100 and a.\"ID\" = @id", new { id = 10 }); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a", sql); + query.ToList(); + } + [Fact] + public void WhereExists() + { + var sql2222 = select.Where(a => select.Where(b => b.Id == a.Id).Any()).ToList(); + + sql2222 = select.Where(a => + select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id) + + //.Offset(a.Id) + + .Any() + ).Any() + ).ToList(); + } + [Fact] + public void GroupBy() + { + var groupby = select.From((s, b, c) => s + .Where(a => a.Id == 1) + ) + .GroupBy((a, b, c) => new { tt2 = a.Title.Substring(0, 2), mod4 = a.Id % 4 }) + .Having(a => a.Count() > 0 && a.Avg(a.Key.mod4) > 0 && a.Max(a.Key.mod4) > 0) + .Having(a => a.Count() < 300 || a.Avg(a.Key.mod4) < 100) + .OrderBy(a => a.Key.tt2) + .OrderByDescending(a => a.Count()) + .Offset(10) + .Limit(2) + .Count(out var trycount) + .ToList(a => new + { + a.Key.tt2, + cou1 = a.Count(), + cou2 = a.Count(a.Value.Item3.Id), + arg1 = a.Avg(a.Key.mod4), + ccc2 = a.Key.tt2 ?? "now()", + //ccc = Convert.ToDateTime("now()"), partby = Convert.ToDecimal("sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)") + ccc3 = a.Max(a.Value.Item3.Id) + }); + + var testpid1 = g.shentong.Insert().AppendData(new TestTypeInfo { Name = "Name" + DateTime.Now.ToString("yyyyMMddHHmmss") }).ExecuteIdentity(); + g.shentong.Insert().AppendData(new TestInfo { Title = "Title" + DateTime.Now.ToString("yyyyMMddHHmmss"), CreateTime = DateTime.Now, TypeGuid = (int)testpid1 }).ExecuteAffrows(); + + var fkfjfj = select.GroupBy(a => a.Title) + .ToList(a => a.Sum(a.Value.TypeGuid)); + + var aggsql1 = select + .GroupBy(a => a.Title) + .ToSql(b => new + { + b.Key, + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid) + }); + var aggtolist1 = select + .GroupBy(a => a.Title) + .ToList(b => new + { + b.Key, + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid) + }); + var aggtolist11 = select + .GroupBy(a => a.Title) + .ToDictionary(b => new + { + b.Key, + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid) + }); + + var aggsql2 = select + .GroupBy(a => new { a.Title, yyyy = string.Concat(a.CreateTime.Year, '-', a.CreateTime.Month) }) + .ToSql(b => new + { + b.Key.Title, + b.Key.yyyy, + + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid) + }); + var aggtolist2 = select + .GroupBy(a => new { a.Title, yyyy = string.Concat(a.CreateTime.Year, '-', a.CreateTime.Month) }) + .ToList(b => new + { + b.Key.Title, + b.Key.yyyy, + + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid) + }); + var aggtolist22 = select + .GroupBy(a => new { a.Title, yyyy = string.Concat(a.CreateTime.Year, '-', a.CreateTime.Month) }) + .ToDictionary(b => new + { + b.Key.Title, + b.Key.yyyy, + + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid) + }); + + var aggsql3 = select + .GroupBy(a => a.Title) + .ToSql(b => new + { + b.Key, + cou = b.Count(), + sum2 = b.Sum(b.Value.TypeGuid), + sum3 = b.Sum(b.Value.Type.Parent.Id) + }); + } + [Fact] + public void ToAggregate() + { + var sql = select.ToAggregate(a => new { sum = a.Sum(a.Key.Id + 11.11), avg = a.Avg(a.Key.Id), count = a.Count(), max = a.Max(a.Key.Id), min = a.Min(a.Key.Id) }); + } + [Fact] + public void OrderBy() + { + var sql = select.OrderBy(a => new Random().NextDouble()).ToList(); + } + [Fact] + public void Skip_Offset() + { + var sql = select.Offset(10).Limit(10).ToList(); + } + [Fact] + public void Take_Limit() + { + var sql = select.Limit(10).ToList(); + } + [Fact] + public void Page() + { + var sql1 = select.Page(1, 10).ToList(); + var sql2 = select.Page(2, 10).ToList(); + var sql3 = select.Page(3, 10).ToList(); + + var sql11 = select.OrderBy(a => new Random().NextDouble()).Page(1, 10).ToList(); + var sql22 = select.OrderBy(a => new Random().NextDouble()).Page(2, 10).ToList(); + var sql33 = select.OrderBy(a => new Random().NextDouble()).Page(3, 10).ToList(); + } + [Fact] + public void Distinct() + { + var t1 = select.Distinct().ToList(a => a.Title); + var t2 = select.Distinct().Limit(10).ToList(a => a.Title); + } + + [Fact] + public void Sum() + { + var subquery = select.ToSql(a => new + { + all = a, + count = (long)select.As("b").Sum(b => b.Id) + }); + Assert.Equal(@"SELECT a.""ID"" as1, a.""CLICKS"" as2, a.""TYPEGUID"" as3, a.""TITLE"" as4, a.""CREATETIME"" as5, (SELECT sum(b.""ID"") + FROM ""TB_TOPIC"" b + limit 1) as6 +FROM ""TB_TOPIC"" a", subquery); + var subqueryList = select.ToList(a => new + { + all = a, + count = (long)select.As("b").Sum(b => b.Id) + }); + } + [Fact] + public void Min() + { + var subquery = select.ToSql(a => new + { + all = a, + count = select.As("b").Min(b => b.Id) + }); + Assert.Equal(@"SELECT a.""ID"" as1, a.""CLICKS"" as2, a.""TYPEGUID"" as3, a.""TITLE"" as4, a.""CREATETIME"" as5, (SELECT min(b.""ID"") + FROM ""TB_TOPIC"" b + limit 1) as6 +FROM ""TB_TOPIC"" a", subquery); + var subqueryList = select.ToList(a => new + { + all = a, + count = select.As("b").Min(b => b.Id) + }); + } + [Fact] + public void Max() + { + var subquery = select.ToSql(a => new + { + all = a, + count = select.As("b").Max(b => b.Id) + }); + Assert.Equal(@"SELECT a.""ID"" as1, a.""CLICKS"" as2, a.""TYPEGUID"" as3, a.""TITLE"" as4, a.""CREATETIME"" as5, (SELECT max(b.""ID"") + FROM ""TB_TOPIC"" b + limit 1) as6 +FROM ""TB_TOPIC"" a", subquery); + var subqueryList = select.ToList(a => new + { + all = a, + count = select.As("b").Max(b => b.Id) + }); + } + [Fact] + public void Avg() + { + var subquery = select.ToSql(a => new + { + all = a, + count = select.As("b").Avg(b => b.Id) + }); + Assert.Equal(@"SELECT a.""ID"" as1, a.""CLICKS"" as2, a.""TYPEGUID"" as3, a.""TITLE"" as4, a.""CREATETIME"" as5, (SELECT avg(b.""ID"") + FROM ""TB_TOPIC"" b + limit 1) as6 +FROM ""TB_TOPIC"" a", subquery); + var subqueryList = select.ToList(a => new + { + all = a, + count = select.As("b").Avg(b => b.Id) + }); + } + [Fact] + public void WhereIn() + { + var subquery = select.Where(a => select.As("b").ToList(b => b.Title).Contains(a.Id.ToString())).ToSql(); + Assert.Equal(@"SELECT a.""ID"", a.""CLICKS"", a.""TYPEGUID"", a.""TITLE"", a.""CREATETIME"" +FROM ""TB_TOPIC"" a +WHERE ((((a.""ID"")::text) in (SELECT b.""TITLE"" + FROM ""TB_TOPIC"" b)))", subquery); + var subqueryList = select.Where(a => select.As("b").ToList(b => b.Title).Contains(a.Id.ToString())).ToList(); + } + [Fact] + public void As() + { + } + + [Fact] + public void AsTable() + { + + var listt = select.AsTable((a, b) => "(select * from tb_topic where clicks > 10)").Page(1, 10).ToList(); + + Func tableRule = (type, oldname) => + { + if (type == typeof(Topic)) return oldname + "AsTable1"; + else if (type == typeof(TestTypeInfo)) return oldname + "AsTable2"; + return oldname + "AsTable"; + }; + + //����е�������a.Type��a.Type.Parent ���ǵ������� + var query = select.LeftJoin(a => a.Type.Guid == a.TypeGuid).AsTable(tableRule); + var sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\"", sql); + + query = select.LeftJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx").AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx'", sql); + + query = select.LeftJoin(a => a.Type.Guid == a.TypeGuid && a.Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10).AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFOASTABLE\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + + //���û�е������� + query = select.LeftJoin((a, b) => b.Guid == a.TypeGuid).AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" b ON b.\"GUID\" = a.\"TYPEGUID\"", sql); + + query = select.LeftJoin((a, b) => b.Guid == a.TypeGuid && b.Name == "xxx").AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" b ON b.\"GUID\" = a.\"TYPEGUID\" AND b.\"NAME\" = 'xxx'", sql); + + query = select.LeftJoin((a, a__Type) => a__Type.Guid == a.TypeGuid && a__Type.Name == "xxx").Where(a => a.Type.Parent.Id == 10).AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" AND a__Type.\"NAME\" = 'xxx' LEFT JOIN \"TESTTYPEPARENTINFOASTABLE\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\" WHERE (a__Type__Parent.\"ID\" = 10)", sql); + + //������� + query = select + .LeftJoin(a => a.Type.Guid == a.TypeGuid) + .LeftJoin(a => a.Type.Parent.Id == a.Type.ParentId).AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" LEFT JOIN \"TESTTYPEPARENTINFOASTABLE\" a__Type__Parent ON a__Type__Parent.\"ID\" = a__Type.\"PARENTID\"", sql); + + query = select + .LeftJoin((a, a__Type) => a__Type.Guid == a.TypeGuid) + .LeftJoin((a, c) => c.Id == a.Type.ParentId).AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a__Type.\"GUID\", a__Type.\"PARENTID\", a__Type.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" a__Type ON a__Type.\"GUID\" = a.\"TYPEGUID\" LEFT JOIN \"TESTTYPEPARENTINFOASTABLE\" c ON c.\"ID\" = a__Type.\"PARENTID\"", sql); + + //���û�е�������b��c������ϵ + var query2 = select.From((s, b, c) => s + .LeftJoin(a => a.TypeGuid == b.Guid) + .LeftJoin(a => b.ParentId == c.Id)).AsTable(tableRule); + sql = query2.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", b.\"GUID\", b.\"PARENTID\", b.\"NAME\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFOASTABLE2\" b ON a.\"TYPEGUID\" = b.\"GUID\" LEFT JOIN \"TESTTYPEPARENTINFOASTABLE\" c ON b.\"PARENTID\" = c.\"ID\"", sql); + + //������϶����㲻�� + query = select.LeftJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\"").AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\"", sql); + + query = select.LeftJoin("\"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", new { bname = "xxx" }).AsTable(tableRule); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPICASTABLE1\" a LEFT JOIN \"TESTTYPEINFO\" b on b.\"GUID\" = a.\"TYPEGUID\" and b.\"NAME\" = @bname", sql); + + query = select.AsTable((_, old) => old).AsTable((_, old) => old); + sql = query.ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT * from (SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a) ftb UNION ALLSELECT * from (SELECT a.\"ID\", a.\"CLICKS\", a.\"TYPEGUID\", a.\"TITLE\", a.\"CREATETIME\" FROM \"TB_TOPIC\" a) ftb", sql); + query.ToList(); + + query = select.AsTable((_, old) => old).AsTable((_, old) => old); + sql = query.ToSql("count(1) as1").Replace("\r\n", ""); + Assert.Equal("SELECT * from (SELECT count(1) as1 FROM \"TB_TOPIC\" a) ftb UNION ALLSELECT * from (SELECT count(1) as1 FROM \"TB_TOPIC\" a) ftb", sql); + query.Count(); + + select.AsTable((_, old) => old).AsTable((_, old) => old).Max(a => a.Id); + select.AsTable((_, old) => old).AsTable((_, old) => old).Min(a => a.Id); + select.AsTable((_, old) => old).AsTable((_, old) => old).Sum(a => a.Id); + select.AsTable((_, old) => old).AsTable((_, old) => old).Avg(a => a.Id); + + var sqlsss = select + .AsTable((type, old) => type == typeof(Topic) ? $"{old}_1" : null) + .AsTable((type, old) => type == typeof(Topic) ? $"{old}_2" : null) + .ToSql(a => new + { + a.Id, + a.Clicks + }, FieldAliasOptions.AsProperty); + + var slsld3 = select + .AsTable((type, old) => type == typeof(Topic) ? $"({sqlsss})" : null) + .Page(1, 20) + .ToList(a => new + { + a.Id, + a.Clicks + }); + } + + public class TestInclude_OneToManyModel1 + { + [Column(IsIdentity = true)] + public int id { get; set; } + public virtual TestInclude_OneToManyModel2 model2 { get; set; } + + public string m1name { get; set; } + } + public class TestInclude_OneToManyModel2 + { + [Column(IsPrimary = true)] + public int model2id { get; set; } + public virtual TestInclude_OneToManyModel1 model1 { get; set; } + + public string m2setting { get; set; } + + public List childs { get; set; } + } + public class TestInclude_OneToManyModel3 + { + [Column(IsIdentity = true)] + public int id { get; set; } + + public int model2111Idaaa { get; set; } + public string title { get; set; } + + public List childs2 { get; set; } + } + public class TestInclude_OneToManyModel4 + { + [Column(IsIdentity = true)] + public int id { get; set; } + + public int model3333Id333 { get; set; } + public string title444 { get; set; } + } + + [Fact] + public void Include_OneToMany() + { + var model1 = new TestInclude_OneToManyModel1 { m1name = DateTime.Now.Second.ToString() }; + model1.id = (int)g.shentong.Insert(model1).ExecuteIdentity(); + var model2 = new TestInclude_OneToManyModel2 { model2id = model1.id, m2setting = DateTime.Now.Second.ToString() }; + g.shentong.Insert(model2).ExecuteAffrows(); + + var model3_1 = new TestInclude_OneToManyModel3 { model2111Idaaa = model1.id, title = "testmodel3__111" }; + model3_1.id = (int)g.shentong.Insert(model3_1).ExecuteIdentity(); + var model3_2 = new TestInclude_OneToManyModel3 { model2111Idaaa = model1.id, title = "testmodel3__222" }; + model3_2.id = (int)g.shentong.Insert(model3_2).ExecuteIdentity(); + var model3_3 = new TestInclude_OneToManyModel3 { model2111Idaaa = model1.id, title = "testmodel3__333" }; + model3_3.id = (int)g.shentong.Insert(model3_2).ExecuteIdentity(); + + var model4s = new[] { + new TestInclude_OneToManyModel4{ model3333Id333 = model3_1.id, title444 = "testmodel3_4__111" }, + new TestInclude_OneToManyModel4{ model3333Id333 = model3_1.id, title444 = "testmodel3_4__222" }, + new TestInclude_OneToManyModel4{ model3333Id333 = model3_2.id, title444 = "testmodel3_4__111" }, + new TestInclude_OneToManyModel4{ model3333Id333 = model3_2.id, title444 = "testmodel3_4__222" }, + new TestInclude_OneToManyModel4{ model3333Id333 = model3_2.id, title444 = "testmodel3_4__333" } + }; + Assert.Equal(5, g.shentong.Insert(model4s).ExecuteAffrows()); + + var t0 = g.shentong.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t1 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t2 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); + + var t00 = g.shentong.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var t11 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(); + + var t22 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(); + + //---- Select ---- + + var at0 = g.shentong.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var at1 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.id <= model1.id) + .ToList(); + + var at2 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id }), + then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id).Select(m4 => new TestInclude_OneToManyModel4 { id = m4.id }))) + .Where(a => a.id <= model1.id) + .ToList(); + + var at00 = g.shentong.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.model2id <= model1.id) + .ToList(); + + var at11 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.id <= model1.id) + .ToList(); + + var at22 = g.shentong.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id }), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id).Select(m4 => new TestInclude_OneToManyModel4 { id = m4.id }))) + .Where(a => a.id <= model1.id) + .ToList(); + } + + public class TestInclude_OneToManyModel11 + { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2id { get; set; } + public string m3setting { get; set; } + public TestInclude_OneToManyModel22 model2 { get; set; } + public string m1name { get; set; } + } + + public class TestInclude_OneToManyModel22 + { + [Column(IsIdentity = true)] + public int id { get; set; } + public string m2setting { get; set; } + public string aaa { get; set; } + public string bbb { get; set; } + public List childs { get; set; } + } + public class TestInclude_OneToManyModel33 + { + [Column(IsIdentity = true)] + public int id { get; set; } + public int model2Id { get; set; } + public string title { get; set; } + public string setting { get; set; } + } + [Fact] + public void Include_OneToMany2() + { + string setting = "x"; + var model2 = new TestInclude_OneToManyModel22 { m2setting = DateTime.Now.Second.ToString(), aaa = "aaa" + DateTime.Now.Second, bbb = "bbb" + DateTime.Now.Second }; + model2.id = (int)g.shentong.Insert(model2).ExecuteIdentity(); + + var model3s = new[] + { + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__111", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__222", setting = setting}, + new TestInclude_OneToManyModel33 {model2Id = model2.id, title = "testmodel3__333", setting = setting} + }; + Assert.Equal(3, g.shentong.Insert(model3s).ExecuteAffrows()); + + var model1 = new TestInclude_OneToManyModel11 { m1name = DateTime.Now.Second.ToString(), model2id = model2.id, m3setting = setting }; + model1.id = (int)g.shentong.Insert(model1).ExecuteIdentity(); + + var t1 = g.shentong.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + + var t11 = g.shentong.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting)) + .Where(a => a.id <= model1.id) + .ToList(true); + + //---- Select ---- + + var at1 = g.shentong.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting).Select(m3 => new TestInclude_OneToManyModel33 { title = m3.title })) + .Where(a => a.id <= model1.id) + .ToList(true); + + var at11 = g.shentong.Select() + .LeftJoin(a => a.model2id == a.model2.id) + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2Id == a.model2.id && m3.setting == a.m3setting).Select(m3 => new TestInclude_OneToManyModel33 { title = m3.title })) + .Where(a => a.id <= model1.id) + .ToList(true); + } + + [Fact] + public void Include_OneToChilds() + { + var tag1 = new Tag + { + Ddd = DateTime.Now.Second, + Name = "test_oneToChilds_01_中国" + }; + tag1.Id = (int)g.shentong.Insert(tag1).ExecuteIdentity(); + var tag1_1 = new Tag + { + Parent_id = tag1.Id, + Ddd = DateTime.Now.Second, + Name = "test_oneToChilds_01_北京" + }; + tag1_1.Id = (int)g.shentong.Insert(tag1_1).ExecuteIdentity(); + var tag1_2 = new Tag + { + Parent_id = tag1.Id, + Ddd = DateTime.Now.Second, + Name = "test_oneToChilds_01_上海" + }; + tag1_2.Id = (int)g.shentong.Insert(tag1_2).ExecuteIdentity(); + + var tag2 = new Tag + { + Ddd = DateTime.Now.Second, + Name = "test_oneToChilds_02_美国" + }; + tag2.Id = (int)g.shentong.Insert(tag2).ExecuteIdentity(); + var tag2_1 = new Tag + { + Parent_id = tag2.Id, + Ddd = DateTime.Now.Second, + Name = "test_oneToChilds_02_纽约" + }; + tag2_1.Id = (int)g.shentong.Insert(tag2_1).ExecuteIdentity(); + var tag2_2 = new Tag + { + Parent_id = tag2.Id, + Ddd = DateTime.Now.Second, + Name = "test_oneToChilds_02_华盛顿" + }; + tag2_2.Id = (int)g.shentong.Insert(tag2_2).ExecuteIdentity(); + + var tags0 = g.shentong.Select() + .Include(a => a.Parent) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags1 = g.shentong.Select() + .IncludeMany(a => a.Tags) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags2 = g.shentong.Select() + .IncludeMany(a => a.Tags, + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags3 = g.shentong.Select() + .IncludeMany(a => a.Tags, + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs).IncludeMany(a => a.Tags)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags11 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags22 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var tags33 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1)).IncludeMany(a => a.Tags.Take(1))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1)) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + // --- Select --- + + var atags0 = g.shentong.Select() + .Include(a => a.Parent) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var atags1 = g.shentong.Select() + .IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name })) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var atags2 = g.shentong.Select() + .IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs)) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var atags3 = g.shentong.Select() + .IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Select(b => new Song { Id = b.Id, Title = b.Title })).IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name }))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var atags11 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1).Select(b => new Tag { Id = b.Id, Name = b.Name })) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var atags22 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1).Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title }))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + + var atags33 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1).Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.Include(a => a.Parent).IncludeMany(a => a.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title })).IncludeMany(a => a.Tags.Take(1).Select(b => new Tag { Id = b.Id, Name = b.Name }))) + .Include(a => a.Parent) + .IncludeMany(a => a.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Id == tag1.Id || a.Id == tag2.Id) + .ToList(); + } + + [Fact] + public void Include_ManyToMany() + { + + var tag1 = new Tag + { + Ddd = DateTime.Now.Second, + Name = "test_manytoMany_01_中国" + }; + tag1.Id = (int)g.shentong.Insert(tag1).ExecuteIdentity(); + var tag2 = new Tag + { + Ddd = DateTime.Now.Second, + Name = "test_manytoMany_02_美国" + }; + tag2.Id = (int)g.shentong.Insert(tag2).ExecuteIdentity(); + var tag3 = new Tag + { + Ddd = DateTime.Now.Second, + Name = "test_manytoMany_03_日本" + }; + tag3.Id = (int)g.shentong.Insert(tag3).ExecuteIdentity(); + + var song1 = new Song + { + Create_time = DateTime.Now, + Title = "test_manytoMany_01_我是中国人.mp3", + Url = "http://ww.baidu.com/" + }; + song1.Id = (int)g.shentong.Insert(song1).ExecuteIdentity(); + var song2 = new Song + { + Create_time = DateTime.Now, + Title = "test_manytoMany_02_爱你一万年.mp3", + Url = "http://ww.163.com/" + }; + song2.Id = (int)g.shentong.Insert(song2).ExecuteIdentity(); + var song3 = new Song + { + Create_time = DateTime.Now, + Title = "test_manytoMany_03_千年等一回.mp3", + Url = "http://ww.sina.com/" + }; + song3.Id = (int)g.shentong.Insert(song3).ExecuteIdentity(); + + g.shentong.Insert(new Song_tag { Song_id = song1.Id, Tag_id = tag1.Id }).ExecuteAffrows(); + g.shentong.Insert(new Song_tag { Song_id = song2.Id, Tag_id = tag1.Id }).ExecuteAffrows(); + g.shentong.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag1.Id }).ExecuteAffrows(); + g.shentong.Insert(new Song_tag { Song_id = song1.Id, Tag_id = tag2.Id }).ExecuteAffrows(); + g.shentong.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); + g.shentong.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); + + var songs1 = g.shentong.Select() + .IncludeMany(a => a.Tags) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); + + var songs2 = g.shentong.Select() + .IncludeMany(a => a.Tags, + then => then.IncludeMany(t => t.Songs)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs2.Count); + Assert.Equal(2, songs2[0].Tags.Count); + Assert.Equal(1, songs2[1].Tags.Count); + Assert.Equal(3, songs2[2].Tags.Count); + + var tags3 = g.shentong.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + + var songs11 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1)) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var songs22 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1), + then => then.IncludeMany(t => t.Songs.Take(1))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var tags33 = g.shentong.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1)) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + // --- Select --- + + new List(new[] { song1, song2, song3 }).IncludeMany(g.shentong, a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name })); + + var asongs1 = g.shentong.Select() + .IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name })) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs1.Count); + Assert.Equal(2, songs1[0].Tags.Count); + Assert.Equal(1, songs1[1].Tags.Count); + Assert.Equal(3, songs1[2].Tags.Count); + + var asongs2 = g.shentong.Select() + .IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.IncludeMany(t => t.Songs.Select(b => new Song { Id = b.Id, Title = b.Title }))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs2.Count); + Assert.Equal(2, songs2[0].Tags.Count); + Assert.Equal(1, songs2[1].Tags.Count); + Assert.Equal(3, songs2[2].Tags.Count); + + var atags3 = g.shentong.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + + + var asongs11 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1).Select(b => new Tag { Id = b.Id, Name = b.Name })) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs11.Count); + Assert.Equal(1, songs11[0].Tags.Count); + Assert.Equal(1, songs11[1].Tags.Count); + Assert.Equal(1, songs11[2].Tags.Count); + + var asongs22 = g.shentong.Select() + .IncludeMany(a => a.Tags.Take(1).Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.IncludeMany(t => t.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title }))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(); + Assert.Equal(3, songs22.Count); + Assert.Equal(1, songs22[0].Tags.Count); + Assert.Equal(1, songs22[1].Tags.Count); + Assert.Equal(1, songs22[2].Tags.Count); + + var atags33 = g.shentong.Select() + .Include(a => a.Tag.Parent) + .IncludeMany(a => a.Tag.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title })) + .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) + .ToList(true); + } + + public class ToDel1Pk + { + [Column(IsIdentity = true)] + public int id { get; set; } + public string name { get; set; } + } + public class ToDel2Pk + { + [Column(IsPrimary = true)] + public Guid pk1 { get; set; } + [Column(IsPrimary = true)] + public string pk2 { get; set; } + public string name { get; set; } + } + public class ToDel3Pk + { + [Column(IsPrimary = true)] + public Guid pk1 { get; set; } + [Column(IsPrimary = true)] + public int pk2 { get; set; } + [Column(IsPrimary = true)] + public string pk3 { get; set; } + public string name { get; set; } + } + [Fact] + public void ToDelete() + { + g.shentong.Select().ToDelete().ExecuteAffrows(); + Assert.Equal(0, g.shentong.Select().Count()); + g.shentong.Insert(new[] { + new ToDel1Pk{ name = "name1"}, + new ToDel1Pk{ name = "name2"}, + new ToDel1Pk{ name = "nick1"}, + new ToDel1Pk{ name = "nick2"}, + new ToDel1Pk{ name = "nick3"} + }).ExecuteAffrows(); + Assert.Equal(2, g.shentong.Select().Where(a => a.name.StartsWith("name")).ToDelete().ExecuteAffrows()); + Assert.Equal(3, g.shentong.Select().Count()); + Assert.Equal(3, g.shentong.Select().Where(a => a.name.StartsWith("nick")).Count()); + + g.shentong.Select().ToDelete().ExecuteAffrows(); + Assert.Equal(0, g.shentong.Select().Count()); + g.shentong.Insert(new[] { + new ToDel2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "name1"}, + new ToDel2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "name2"}, + new ToDel2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "nick1"}, + new ToDel2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "nick2"}, + new ToDel2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "nick3"} + }).ExecuteAffrows(); + Assert.Equal(2, g.shentong.Select().Where(a => a.name.StartsWith("name")).ToDelete().ExecuteAffrows()); + Assert.Equal(3, g.shentong.Select().Count()); + Assert.Equal(3, g.shentong.Select().Where(a => a.name.StartsWith("nick")).Count()); + + g.shentong.Select().ToDelete().ExecuteAffrows(); + Assert.Equal(0, g.shentong.Select().Count()); + g.shentong.Insert(new[] { + new ToDel3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "name1"}, + new ToDel3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "name2"}, + new ToDel3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "nick1"}, + new ToDel3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "nick2"}, + new ToDel3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "nick3"} + }).ExecuteAffrows(); + Assert.Equal(2, g.shentong.Select().Where(a => a.name.StartsWith("name")).ToDelete().ExecuteAffrows()); + Assert.Equal(3, g.shentong.Select().Count()); + Assert.Equal(3, g.shentong.Select().Where(a => a.name.StartsWith("nick")).Count()); + } + + public class ToUpd1Pk + { + [Column(IsIdentity = true)] + public int id { get; set; } + public string name { get; set; } + } + public class ToUpd2Pk + { + [Column(IsPrimary = true)] + public Guid pk1 { get; set; } + [Column(IsPrimary = true)] + public string pk2 { get; set; } + public string name { get; set; } + } + public class ToUpd3Pk + { + [Column(IsPrimary = true)] + public Guid pk1 { get; set; } + [Column(IsPrimary = true)] + public int pk2 { get; set; } + [Column(IsPrimary = true)] + public string pk3 { get; set; } + public string name { get; set; } + } + [Fact] + public void ToUpdate() + { + g.shentong.Select().ToDelete().ExecuteAffrows(); + Assert.Equal(0, g.shentong.Select().Count()); + g.shentong.Insert(new[] { + new ToUpd1Pk{ name = "name1"}, + new ToUpd1Pk{ name = "name2"}, + new ToUpd1Pk{ name = "nick1"}, + new ToUpd1Pk{ name = "nick2"}, + new ToUpd1Pk{ name = "nick3"} + }).ExecuteAffrows(); + Assert.Equal(2, g.shentong.Select().Where(a => a.name.StartsWith("name")).ToUpdate().Set(a => a.name, "nick?").ExecuteAffrows()); + Assert.Equal(5, g.shentong.Select().Count()); + Assert.Equal(5, g.shentong.Select().Where(a => a.name.StartsWith("nick")).Count()); + + g.shentong.Select().ToDelete().ExecuteAffrows(); + Assert.Equal(0, g.shentong.Select().Count()); + g.shentong.Insert(new[] { + new ToUpd2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "name1"}, + new ToUpd2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "name2"}, + new ToUpd2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "nick1"}, + new ToUpd2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "nick2"}, + new ToUpd2Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = "pk2", name = "nick3"} + }).ExecuteAffrows(); + Assert.Equal(2, g.shentong.Select().Where(a => a.name.StartsWith("name")).ToUpdate().Set(a => a.name, "nick?").ExecuteAffrows()); + Assert.Equal(5, g.shentong.Select().Count()); + Assert.Equal(5, g.shentong.Select().Where(a => a.name.StartsWith("nick")).Count()); + + g.shentong.Select().ToDelete().ExecuteAffrows(); + Assert.Equal(0, g.shentong.Select().Count()); + g.shentong.Insert(new[] { + new ToUpd3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "name1"}, + new ToUpd3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "name2"}, + new ToUpd3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "nick1"}, + new ToUpd3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "nick2"}, + new ToUpd3Pk{ pk1 = FreeUtil.NewMongodbId(), pk2 = 1, pk3 = "pk3", name = "nick3"} + }).ExecuteAffrows(); + Assert.Equal(2, g.shentong.Select().Where(a => a.name.StartsWith("name")).ToUpdate().Set(a => a.name, "nick?").ExecuteAffrows()); + Assert.Equal(5, g.shentong.Select().Count()); + Assert.Equal(5, g.shentong.Select().Where(a => a.name.StartsWith("nick")).Count()); + } + + [Fact] + public void ForUpdate() + { + var orm = g.shentong; + + Assert.Equal("安全起见,请务必在事务开启之后,再使用 ForUpdate", + Assert.Throws(() => orm.Select().ForUpdate().Limit(1).ToList())?.Message); + + orm.Transaction(() => + { + var sql = orm.Select().ForUpdate().Limit(1).ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"NAME\" FROM \"TOUPD1PK\" a limit 1 for update", sql); + orm.Select().ForUpdate().Limit(1).ToList(); + + sql = orm.Select().ForUpdate(true).Limit(1).ToSql().Replace("\r\n", ""); + Assert.Equal("SELECT a.\"ID\", a.\"NAME\" FROM \"TOUPD1PK\" a limit 1 for update", sql); + orm.Select().ForUpdate(true).Limit(1).ToList(); + }); + } + [Fact] + public void ToTreeList() + { + var fsql = g.shentong; + fsql.Delete().Where("1=1").ExecuteAffrows(); + var repo = fsql.GetRepository(); + repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; + repo.DbContextOptions.NoneParameter = true; + repo.Insert(new VM_District_Child + { + Code = "100000", + Name = "中国", + Childs = new List(new[] { + new VM_District_Child + { + Code = "110000", + Name = "北京市", + Childs = new List(new[] { + new VM_District_Child{ Code="110100", Name = "北京市" }, + new VM_District_Child{ Code="110101", Name = "东城区" }, + }) + } + }) + }); + + var t1 = fsql.Select() + .InnerJoin(a => a.ParentCode == a.Parent.Code) + .Where(a => a.Code == "110101") + .ToList(true); + Assert.Single(t1); + Assert.Equal("110101", t1[0].Code); + Assert.NotNull(t1[0].Parent); + Assert.Equal("110000", t1[0].Parent.Code); + + var t2 = fsql.Select() + .InnerJoin(a => a.ParentCode == a.Parent.Code) + .InnerJoin(a => a.Parent.ParentCode == a.Parent.Parent.Code) + .Where(a => a.Code == "110101") + .ToList(true); + Assert.Single(t2); + Assert.Equal("110101", t2[0].Code); + Assert.NotNull(t2[0].Parent); + Assert.Equal("110000", t2[0].Parent.Code); + Assert.NotNull(t2[0].Parent.Parent); + Assert.Equal("100000", t2[0].Parent.Parent.Code); + + var t3 = fsql.Select().ToTreeList(); + Assert.Single(t3); + Assert.Equal("100000", t3[0].Code); + Assert.Single(t3[0].Childs); + Assert.Equal("110000", t3[0].Childs[0].Code); + Assert.Equal(2, t3[0].Childs[0].Childs.Count); + Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code); + Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code); + } + + [Table(Name = "D_District")] + public class BaseDistrict + { + [Column(IsPrimary = true, StringLength = 6)] + public string Code { get; set; } + + [Column(StringLength = 20, IsNullable = false)] + public string Name { get; set; } + + [Column(StringLength = 6)] + public virtual string ParentCode { get; set; } + } + [Table(Name = "D_District", DisableSyncStructure = true)] + public class VM_District_Child : BaseDistrict + { + public override string ParentCode { get => base.ParentCode; set => base.ParentCode = value; } + + [Navigate(nameof(ParentCode))] + public List Childs { get; set; } + } + [Table(Name = "D_District", DisableSyncStructure = true)] + public class VM_District_Parent : BaseDistrict + { + public override string ParentCode { get => base.ParentCode; set => base.ParentCode = value; } + + [Navigate(nameof(ParentCode))] + public VM_District_Parent Parent { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongUpdateTest.cs new file mode 100644 index 00000000..549114dc --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/Curd/ShenTongUpdateTest.cs @@ -0,0 +1,189 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongUpdateTest + { + IUpdate update => g.shentong.Update(); + + [Table(Name = "tb_topic")] + class Topic + { + [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 Dywhere() + { + Assert.Null(g.shentong.Update().ToSql()); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='test' \r\nWHERE (\"ID\" = 1 OR \"ID\" = 2)", g.shentong.Update(new[] { 1, 2 }).SetRaw("title='test'").ToSql()); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='test1' \r\nWHERE (\"ID\" = 1)", g.shentong.Update(new Topic { Id = 1, Title = "test" }).SetRaw("title='test1'").ToSql()); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='test1' \r\nWHERE (\"ID\" = 1 OR \"ID\" = 2)", g.shentong.Update(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).SetRaw("title='test1'").ToSql()); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='test1' \r\nWHERE (\"ID\" = 1)", g.shentong.Update(new { id = 1 }).SetRaw("title='test1'").ToSql()); + } + + [Fact] + public void SetSource() + { + var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = @p_0, \"TITLE\" = @p_1, \"CREATETIME\" = @p_2 WHERE (\"ID\" = 1)", sql); + + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + items[0].Clicks = null; + + sql = update.SetSource(items).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = CASE \"ID\" WHEN 1 THEN @p_0 WHEN 2 THEN @p_1 WHEN 3 THEN @p_2 WHEN 4 THEN @p_3 WHEN 5 THEN @p_4 WHEN 6 THEN @p_5 WHEN 7 THEN @p_6 WHEN 8 THEN @p_7 WHEN 9 THEN @p_8 WHEN 10 THEN @p_9 END, \"TITLE\" = CASE \"ID\" WHEN 1 THEN @p_10 WHEN 2 THEN @p_11 WHEN 3 THEN @p_12 WHEN 4 THEN @p_13 WHEN 5 THEN @p_14 WHEN 6 THEN @p_15 WHEN 7 THEN @p_16 WHEN 8 THEN @p_17 WHEN 9 THEN @p_18 WHEN 10 THEN @p_19 END, \"CREATETIME\" = CASE \"ID\" WHEN 1 THEN @p_20 WHEN 2 THEN @p_21 WHEN 3 THEN @p_22 WHEN 4 THEN @p_23 WHEN 5 THEN @p_24 WHEN 6 THEN @p_25 WHEN 7 THEN @p_26 WHEN 8 THEN @p_27 WHEN 9 THEN @p_28 WHEN 10 THEN @p_29 END WHERE (\"ID\" IN (1,2,3,4,5,6,7,8,9,10))", sql); + + sql = update.SetSource(items).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"TITLE\" = CASE \"ID\" WHEN 1 THEN @p_0 WHEN 2 THEN @p_1 WHEN 3 THEN @p_2 WHEN 4 THEN @p_3 WHEN 5 THEN @p_4 WHEN 6 THEN @p_5 WHEN 7 THEN @p_6 WHEN 8 THEN @p_7 WHEN 9 THEN @p_8 WHEN 10 THEN @p_9 END WHERE (\"ID\" IN (1,2,3,4,5,6,7,8,9,10))", sql); + + sql = update.SetSource(items).Set(a => a.CreateTime, new DateTime(2020, 1, 1)).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CREATETIME\" = @p_0 WHERE (\"ID\" IN (1,2,3,4,5,6,7,8,9,10))", sql); + + sql = g.shentong.Update().SetSource(new[] { + new ts_source_mpk { id1 = 1, id2 = 7, xx = "a1" }, + new ts_source_mpk { id1 = 1, id2 = 8, xx = "b122" } + }).NoneParameter().ToSql().Replace("\r\n", ""); + } + public class ts_source_mpk + { + [Column(IsPrimary = true)] + public int id1 { get; set; } + [Column(IsPrimary = true)] + public int id2 { get; set; } + public string xx { get; set; } + } + [Fact] + public void SetSourceIgnore() + { + Assert.Equal("UPDATE \"TSSI01\" SET \"TINT\" = 10 WHERE (\"ID\" = '00000000-0000-0000-0000-000000000000')", + g.shentong.Update().NoneParameter() + .SetSourceIgnore(new tssi01 { id = Guid.Empty, tint = 10 }, col => col == null).ToSql().Replace("\r\n", "")); + } + public class tssi01 + { + [Column(CanUpdate = false)] + public Guid id { get; set; } + public int tint { get; set; } + public string title { get; set; } + } + [Fact] + public void IgnoreColumns() + { + var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"TITLE\" = @p_0 WHERE (\"ID\" = 1)", sql); + } + [Fact] + public void UpdateColumns() + { + var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).UpdateColumns(a => a.Title).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"TITLE\" = @p_0 WHERE (\"ID\" = 1)", sql); + } + [Fact] + public void Set() + { + var sql = update.Where(a => a.Id == 1).Set(a => a.Title, "newtitle").ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"TITLE\" = @p_0 WHERE (\"ID\" = 1)", sql); + + sql = update.Where(a => a.Id == 1).Set(a => a.Title, "newtitle").Set(a => a.CreateTime, new DateTime(2020, 1, 1)).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"TITLE\" = @p_0, \"CREATETIME\" = @p_1 WHERE (\"ID\" = 1)", sql); + + sql = update.Set(a => a.Clicks * 10 / 1).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = coalesce(\"CLICKS\", 0) * 10 / 1 WHERE (\"ID\" = 1)", sql); + + sql = update.Set(a => a.Id - 10).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"ID\" = (\"ID\" - 10) WHERE (\"ID\" = 1)", sql); + + int incrv = 10; + sql = update.Set(a => a.Clicks * incrv / 1).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = coalesce(\"CLICKS\", 0) * 10 / 1 WHERE (\"ID\" = 1)", sql); + + sql = update.Set(a => a.Id - incrv).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"ID\" = (\"ID\" - 10) WHERE (\"ID\" = 1)", sql); + + sql = update.Set(a => a.Clicks == a.Clicks * 10 / 1).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = \"CLICKS\" * 10 / 1 WHERE (\"ID\" = 1)", sql); + + var dt2000 = DateTime.Parse("2000-01-01"); + sql = update.Set(a => a.Clicks == (a.CreateTime > dt2000 ? 1 : 2)).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = case when \"CREATETIME\" > '2000-01-01 00:00:00.000000' then 1 else 2 end WHERE (\"ID\" = 1)", sql); + + sql = update.Set(a => a.Id == 10).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"ID\" = 10 WHERE (\"ID\" = 1)", sql); + + sql = update.Set(a => a.Clicks == null).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = NULL WHERE (\"ID\" = 1)", sql); + } + [Fact] + public void SetRaw() + { + var sql = update.Where(a => a.Id == 1).SetRaw("clicks = clicks + @incrClick", new { incrClick = 1 }).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET clicks = clicks + @incrClick WHERE (\"ID\" = 1)", sql); + } + [Fact] + public void SetDto() + { + var sql = update.SetDto(new { clicks = 1, title = "xxx" }).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = @p_0, \"TITLE\" = @p_1 WHERE (\"ID\" = 1)", sql); + sql = update.NoneParameter().SetDto(new { clicks = 1, title = "xxx" }).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = 1, \"TITLE\" = 'xxx' WHERE (\"ID\" = 1)", sql); + + sql = update.SetDto(new Dictionary { ["clicks"] = 1, ["title"] = "xxx" }).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = @p_0, \"TITLE\" = @p_1 WHERE (\"ID\" = 1)", sql); + sql = update.NoneParameter().SetDto(new Dictionary { ["clicks"] = 1, ["title"] = "xxx" }).Where(a => a.Id == 1).ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET \"CLICKS\" = 1, \"TITLE\" = 'xxx' WHERE (\"ID\" = 1)", sql); + } + [Fact] + public void Where() + { + var sql = update.Where(a => a.Id == 1).SetRaw("title='newtitle'").ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='newtitle' WHERE (\"ID\" = 1)", sql); + + sql = update.Where("id = @id", new { id = 1 }).SetRaw("title='newtitle'").ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='newtitle' WHERE (id = @id)", sql); + + var item = new Topic { Id = 1, Title = "newtitle" }; + sql = update.Where(item).SetRaw("title='newtitle'").ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='newtitle' WHERE (\"ID\" = 1)", sql); + + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + sql = update.Where(items).SetRaw("title='newtitle'").ToSql().Replace("\r\n", ""); + Assert.Equal("UPDATE \"TB_TOPIC\" SET title='newtitle' WHERE (\"ID\" IN (1,2,3,4,5,6,7,8,9,10))", sql); + } + [Fact] + public void ExecuteAffrows() + { + var items = new List(); + for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 }); + + update.SetSource(items.First()).NoneParameter().ExecuteAffrows(); + update.SetSource(items).NoneParameter().ExecuteAffrows(); + } + [Fact] + public void ExecuteUpdated() + { + + } + + [Fact] + public void AsTable() + { + Assert.Null(g.shentong.Update().ToSql()); + Assert.Equal("UPDATE \"TB_TOPICASTABLE\" SET title='test' \r\nWHERE (\"ID\" = 1 OR \"ID\" = 2)", g.shentong.Update(new[] { 1, 2 }).SetRaw("title='test'").AsTable(a => "tb_topicAsTable").ToSql()); + Assert.Equal("UPDATE \"TB_TOPICASTABLE\" SET title='test1' \r\nWHERE (\"ID\" = 1)", g.shentong.Update(new Topic { Id = 1, Title = "test" }).SetRaw("title='test1'").AsTable(a => "tb_topicAsTable").ToSql()); + Assert.Equal("UPDATE \"TB_TOPICASTABLE\" SET title='test1' \r\nWHERE (\"ID\" = 1 OR \"ID\" = 2)", g.shentong.Update(new[] { new Topic { Id = 1, Title = "test" }, new Topic { Id = 2, Title = "test" } }).SetRaw("title='test1'").AsTable(a => "tb_topicAsTable").ToSql()); + Assert.Equal("UPDATE \"TB_TOPICASTABLE\" SET title='test1' \r\nWHERE (\"ID\" = 1)", g.shentong.Update(new { id = 1 }).SetRaw("title='test1'").AsTable(a => "tb_topicAsTable").ToSql()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolNullableTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolNullableTest.cs new file mode 100644 index 00000000..8da136d8 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolNullableTest.cs @@ -0,0 +1,1571 @@ +using FreeSql.DataAnnotations; +using System; +using Xunit; + +namespace FreeSql.Tests.ShenTongMapType +{ + public class BoolNullableTest + { + class BoolNullableMap + { + public Guid id { get; set; } + [Column(MapType = typeof(bool))] + public bool? tobool { get; set; } = true; + + [Column(MapType = typeof(sbyte))] + public bool? tosbyte { get; set; } = true; + [Column(MapType = typeof(sbyte?))] + public bool? tosbytenullable { get; set; } = true; + + [Column(MapType = typeof(short))] + public bool? toshort { get; set; } = true; + + [Column(MapType = typeof(short?))] + public bool? toshortnullable { get; set; } = true; + + [Column(MapType = typeof(int))] + public bool? toint { get; set; } = true; + + [Column(MapType = typeof(int?))] + public bool? tointnullable { get; set; } = true; + + [Column(MapType = typeof(long))] + public bool? tolong { get; set; } = true; + [Column(MapType = typeof(long?))] + public bool? tolongnullable { get; set; } = true; + + [Column(MapType = typeof(byte))] + public bool? tobyte { get; set; } = true; + [Column(MapType = typeof(byte?))] + public bool? tobytenullable { get; set; } = true; + + [Column(MapType = typeof(ushort))] + public bool? toushort { get; set; } = true; + + [Column(MapType = typeof(ushort?))] + public bool? toushortnullable { get; set; } = true; + + [Column(MapType = typeof(uint))] + public bool? touint { get; set; } = true; + + [Column(MapType = typeof(uint?))] + public bool? touintnullable { get; set; } = true; + + [Column(MapType = typeof(ulong))] + public bool? toulong { get; set; } = true; + [Column(MapType = typeof(ulong?))] + public bool? toulongnullable { get; set; } = true; + + [Column(MapType = typeof(string))] + public bool? tostring { get; set; } = true; + } + [Fact] + public void Bool() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tobool == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobool, find.tobool); + Assert.Equal(true, find.tobool); + + item = new BoolNullableMap { tobool = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobool, find.tobool); + Assert.Equal(false, find.tobool); + + item = new BoolNullableMap { tobool = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobool == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tobool, find.tobool); + Assert.Equal(false, find.tobool); + + //update all + item.tobool = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobool, find.tobool); + Assert.Equal(true, find.tobool); + + item.tobool = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobool, find.tobool); + Assert.Equal(false, find.tobool); + + item.tobool = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobool == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tobool, find.tobool); + Assert.Equal(false, find.tobool); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobool, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tobool); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobool, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tobool); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobool, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobool == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobool == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tobool); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobool == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobool == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tobool == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void SByte() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tosbyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.Equal(true, find.tosbyte); + + item = new BoolNullableMap { tosbyte = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.Equal(false, find.tosbyte); + + item = new BoolNullableMap { tosbyte = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tosbyte == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tosbyte, find.tosbyte); + Assert.Equal(false, find.tosbyte); + + //update all + item.tosbyte = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.Equal(true, find.tosbyte); + + item.tosbyte = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.Equal(false, find.tosbyte); + + item.tosbyte = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tosbyte == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tosbyte, find.tosbyte); + Assert.Equal(false, find.tosbyte); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbyte, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tosbyte); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbyte, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tosbyte); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbyte, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tosbyte == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tosbyte); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tosbyte == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tosbyte == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tosbyte == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void SByteNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.Equal(true, find.tosbytenullable); + + item = new BoolNullableMap { tosbytenullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.Equal(false, find.tosbytenullable); + + item = new BoolNullableMap { tosbytenullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.Null(find.tosbytenullable); + + //update all + item.tosbytenullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.Equal(true, find.tosbytenullable); + + item.tosbytenullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.Equal(false, find.tosbytenullable); + + item.tosbytenullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.Null(find.tosbytenullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbytenullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tosbytenullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbytenullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tosbytenullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbytenullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.tosbytenullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tosbytenullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tosbytenullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tosbytenullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void Short() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toshort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.Equal(true, find.toshort); + + item = new BoolNullableMap { toshort = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.Equal(false, find.toshort); + + item = new BoolNullableMap { toshort = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toshort == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toshort, find.toshort); + Assert.Equal(false, find.toshort); + + //update all + item.toshort = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.Equal(true, find.toshort); + + item.toshort = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.Equal(false, find.toshort); + + item.toshort = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toshort == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toshort, find.toshort); + Assert.Equal(false, find.toshort); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshort, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toshort); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshort, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toshort); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshort, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toshort == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toshort); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toshort == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toshort == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toshort == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ShortNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.Equal(true, find.toshortnullable); + + item = new BoolNullableMap { toshortnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.Equal(false, find.toshortnullable); + + item = new BoolNullableMap { toshortnullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.Null(find.toshortnullable); + + //update all + item.toshortnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.Equal(true, find.toshortnullable); + + item.toshortnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.Equal(false, find.toshortnullable); + + item.toshortnullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.Null(find.toshortnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshortnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toshortnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshortnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toshortnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshortnullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.toshortnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toshortnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toshortnullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toshortnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void Int() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.Equal(true, find.toint); + + item = new BoolNullableMap { toint = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.Equal(false, find.toint); + + item = new BoolNullableMap { toint = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toint == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toint, find.toint); + Assert.Equal(false, find.toint); + + //update all + item.toint = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.Equal(true, find.toint); + + item.toint = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.Equal(false, find.toint); + + item.toint = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toint == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toint, find.toint); + Assert.Equal(false, find.toint); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toint, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toint); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toint, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toint); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toint, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toint == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toint); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toint == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toint == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toint == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void IntNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tointnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.Equal(true, find.tointnullable); + + item = new BoolNullableMap { tointnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.Equal(false, find.tointnullable); + + item = new BoolNullableMap { tointnullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.Null(find.tointnullable); + + //update all + item.tointnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.Equal(true, find.tointnullable); + + item.tointnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.Equal(false, find.tointnullable); + + item.tointnullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.Null(find.tointnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tointnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tointnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tointnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tointnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tointnullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.tointnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tointnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tointnullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tointnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void Long() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tolong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.Equal(true, find.tolong); + + item = new BoolNullableMap { tolong = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.Equal(false, find.tolong); + + item = new BoolNullableMap { tolong = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tolong == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tolong, find.tolong); + Assert.Equal(false, find.tolong); + + //update all + item.tolong = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.Equal(true, find.tolong); + + item.tolong = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.Equal(false, find.tolong); + + item.tolong = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tolong == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tolong, find.tolong); + Assert.Equal(false, find.tolong); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolong, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tolong); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolong, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tolong); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolong, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tolong == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tolong); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tolong == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tolong == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tolong == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void LongNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.Equal(true, find.tolongnullable); + + item = new BoolNullableMap { tolongnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.Equal(false, find.tolongnullable); + + item = new BoolNullableMap { tolongnullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.Null(find.tolongnullable); + + //update all + item.tolongnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.Equal(true, find.tolongnullable); + + item.tolongnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.Equal(false, find.tolongnullable); + + item.tolongnullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.Null(find.tolongnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolongnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tolongnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolongnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tolongnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolongnullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.tolongnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tolongnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tolongnullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tolongnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void Byte() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tobyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.Equal(true, find.tobyte); + + item = new BoolNullableMap { tobyte = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.Equal(false, find.tobyte); + + item = new BoolNullableMap { tobyte = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobyte == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tobyte, find.tobyte); + Assert.Equal(false, find.tobyte); + + //update all + item.tobyte = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.Equal(true, find.tobyte); + + item.tobyte = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.Equal(false, find.tobyte); + + item.tobyte = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobyte == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.tobyte, find.tobyte); + Assert.Equal(false, find.tobyte); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobyte, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tobyte); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobyte, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tobyte); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobyte, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobyte == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tobyte); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobyte == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobyte == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tobyte == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ByteNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.Equal(true, find.tobytenullable); + + item = new BoolNullableMap { tobytenullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.Equal(false, find.tobytenullable); + + item = new BoolNullableMap { tobytenullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.Null(find.tobytenullable); + + //update all + item.tobytenullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.Equal(true, find.tobytenullable); + + item.tobytenullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.Equal(false, find.tobytenullable); + + item.tobytenullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.Null(find.tobytenullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobytenullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tobytenullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobytenullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tobytenullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobytenullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.tobytenullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobytenullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tobytenullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobytenullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UShort() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toushort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.Equal(true, find.toushort); + + item = new BoolNullableMap { toushort = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.Equal(false, find.toushort); + + item = new BoolNullableMap { toushort = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toushort == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toushort, find.toushort); + Assert.Equal(false, find.toushort); + + //update all + item.toushort = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.Equal(true, find.toushort); + + item.toushort = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.Equal(false, find.toushort); + + item.toushort = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toushort == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toushort, find.toushort); + Assert.Equal(false, find.toushort); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushort, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toushort); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushort, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toushort); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushort, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toushort == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toushort); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toushort == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toushort == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toushort == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UShortNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.Equal(true, find.toushortnullable); + + item = new BoolNullableMap { toushortnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.Equal(false, find.toushortnullable); + + item = new BoolNullableMap { toushortnullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.Null(find.toushortnullable); + + //update all + item.toushortnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.Equal(true, find.toushortnullable); + + item.toushortnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.Equal(false, find.toushortnullable); + + item.toushortnullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.Null(find.toushortnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushortnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toushortnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushortnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toushortnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushortnullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.toushortnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toushortnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toushortnullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toushortnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UInt() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.touint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.Equal(true, find.touint); + + item = new BoolNullableMap { touint = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.Equal(false, find.touint); + + item = new BoolNullableMap { touint = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.touint == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.touint, find.touint); + Assert.Equal(false, find.touint); + + //update all + item.touint = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.Equal(true, find.touint); + + item.touint = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.Equal(false, find.touint); + + item.touint = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.touint == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.touint, find.touint); + Assert.Equal(false, find.touint); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touint, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.touint); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touint, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.touint); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touint, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.touint == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.touint); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.touint == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.touint == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.touint == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UIntNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.touintnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.Equal(true, find.touintnullable); + + item = new BoolNullableMap { touintnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.Equal(false, find.touintnullable); + + item = new BoolNullableMap { touintnullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.Null(find.touintnullable); + + //update all + item.touintnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.Equal(true, find.touintnullable); + + item.touintnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.Equal(false, find.touintnullable); + + item.touintnullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.Null(find.touintnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touintnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.touintnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touintnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.touintnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touintnullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.touintnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.touintnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.touintnullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.touintnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ULong() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toulong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.Equal(true, find.toulong); + + item = new BoolNullableMap { toulong = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.Equal(false, find.toulong); + + item = new BoolNullableMap { toulong = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toulong == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toulong, find.toulong); + Assert.Equal(false, find.toulong); + + //update all + item.toulong = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.Equal(true, find.toulong); + + item.toulong = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.Equal(false, find.toulong); + + item.toulong = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toulong == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.NotEqual(item.toulong, find.toulong); + Assert.Equal(false, find.toulong); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulong, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toulong); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulong, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toulong); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulong, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toulong == null).First()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toulong); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toulong == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toulong == null).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toulong == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ULongNullable() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.Equal(true, find.toulongnullable); + + item = new BoolNullableMap { toulongnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.Equal(false, find.toulongnullable); + + item = new BoolNullableMap { toulongnullable = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.Null(find.toulongnullable); + + //update all + item.toulongnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.Equal(true, find.toulongnullable); + + item.toulongnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.Equal(false, find.toulongnullable); + + item.toulongnullable = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.Null(find.toulongnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulongnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.toulongnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulongnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.toulongnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulongnullable, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.toulongnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toulongnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toulongnullable == null).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toulongnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void TimeSpan() + { + } + [Fact] + public void TimeSpanNullable() + { + } + [Fact] + public void DateTime() + { + } + [Fact] + public void DateTimeNullable() + { + } + + [Fact] + public void ByteArray() + { + } + [Fact] + public void String() + { + //insert + var orm = g.shentong; + var item = new BoolNullableMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tostring == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.Equal(true, find.tostring); + + item = new BoolNullableMap { tostring = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.Equal(false, find.tostring); + + item = new BoolNullableMap { tostring = null }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tostring == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.Null(find.tostring); + + //update all + item.tostring = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.Equal(true, find.tostring); + + item.tostring = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.Equal(false, find.tostring); + + item.tostring = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tostring == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.Null(find.tostring); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tostring, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(true, find.tostring); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tostring, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(false, find.tostring); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tostring, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.tostring == false).First()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.tostring); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tostring == true).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tostring == false).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tostring == null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void Guid() + { + } + [Fact] + public void GuidNullable() + { + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolTest.cs new file mode 100644 index 00000000..d26079a7 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/BoolTest.cs @@ -0,0 +1,1105 @@ +using FreeSql.DataAnnotations; +using System; +using Xunit; + +namespace FreeSql.Tests.ShenTongMapType +{ + public class BoolTest + { + + class BoolMap + { + public Guid id { get; set; } + [Column(MapType = typeof(bool?))] + public bool toboolnullable { get; set; } = true; + + [Column(MapType = typeof(sbyte))] + public bool tosbyte { get; set; } = true; + [Column(MapType = typeof(sbyte?))] + public bool tosbytenullable { get; set; } = true; + + [Column(MapType = typeof(short))] + public bool toshort { get; set; } = true; + + [Column(MapType = typeof(short?))] + public bool toshortnullable { get; set; } = true; + + [Column(MapType = typeof(int))] + public bool toint { get; set; } = true; + + [Column(MapType = typeof(int?))] + public bool tointnullable { get; set; } = true; + + [Column(MapType = typeof(long))] + public bool tolong { get; set; } = true; + [Column(MapType = typeof(long?))] + public bool tolongnullable { get; set; } = true; + + [Column(MapType = typeof(byte))] + public bool tobyte { get; set; } = true; + [Column(MapType = typeof(byte?))] + public bool tobytenullable { get; set; } = true; + + [Column(MapType = typeof(ushort))] + public bool toushort { get; set; } = true; + + [Column(MapType = typeof(ushort?))] + public bool toushortnullable { get; set; } = true; + + [Column(MapType = typeof(uint))] + public bool touint { get; set; } = true; + + [Column(MapType = typeof(uint?))] + public bool touintnullable { get; set; } = true; + + [Column(MapType = typeof(ulong))] + public bool toulong { get; set; } = true; + [Column(MapType = typeof(ulong?))] + public bool toulongnullable { get; set; } = true; + + [Column(MapType = typeof(string))] + public bool tostring { get; set; } = true; + } + + [Fact] + public void BoolNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toboolnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toboolnullable, find.toboolnullable); + Assert.True(find.toboolnullable); + + item = new BoolMap { toboolnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toboolnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toboolnullable, find.toboolnullable); + Assert.False(find.toboolnullable); + + //update all + item.toboolnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toboolnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toboolnullable, find.toboolnullable); + Assert.True(find.toboolnullable); + + item.toboolnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toboolnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toboolnullable, find.toboolnullable); + Assert.False(find.toboolnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toboolnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toboolnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toboolnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toboolnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toboolnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toboolnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toboolnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toboolnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void SByte() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tosbyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.True(find.tosbyte); + + item = new BoolMap { tosbyte = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.False(find.tosbyte); + + //update all + item.tosbyte = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.True(find.tosbyte); + + item.tosbyte = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbyte, find.tosbyte); + Assert.False(find.tosbyte); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbyte, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tosbyte); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbyte, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tosbyte); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tosbyte == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tosbyte == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void SByteNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.True(find.tosbytenullable); + + item = new BoolMap { tosbytenullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.False(find.tosbytenullable); + + //update all + item.tosbytenullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.True(find.tosbytenullable); + + item.tosbytenullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tosbytenullable, find.tosbytenullable); + Assert.False(find.tosbytenullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbytenullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tosbytenullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tosbytenullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tosbytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tosbytenullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tosbytenullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tosbytenullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void Short() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toshort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.True(find.toshort); + + item = new BoolMap { toshort = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.False(find.toshort); + + //update all + item.toshort = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.True(find.toshort); + + item.toshort = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshort, find.toshort); + Assert.False(find.toshort); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshort, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toshort); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshort, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toshort); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toshort == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toshort == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ShortNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.True(find.toshortnullable); + + item = new BoolMap { toshortnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.False(find.toshortnullable); + + //update all + item.toshortnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.True(find.toshortnullable); + + item.toshortnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toshortnullable, find.toshortnullable); + Assert.False(find.toshortnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshortnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toshortnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toshortnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toshortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toshortnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toshortnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toshortnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void Int() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.True(find.toint); + + item = new BoolMap { toint = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.False(find.toint); + + //update all + item.toint = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.True(find.toint); + + item.toint = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toint, find.toint); + Assert.False(find.toint); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toint, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toint); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toint, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toint); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toint == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toint == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void IntNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tointnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.True(find.tointnullable); + + item = new BoolMap { tointnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.False(find.tointnullable); + + //update all + item.tointnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.True(find.tointnullable); + + item.tointnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tointnullable, find.tointnullable); + Assert.False(find.tointnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tointnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tointnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tointnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tointnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tointnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tointnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tointnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void Long() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tolong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.True(find.tolong); + + item = new BoolMap { tolong = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.False(find.tolong); + + //update all + item.tolong = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.True(find.tolong); + + item.tolong = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolong, find.tolong); + Assert.False(find.tolong); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolong, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tolong); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolong, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tolong); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tolong == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tolong == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void LongNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.True(find.tolongnullable); + + item = new BoolMap { tolongnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.False(find.tolongnullable); + + //update all + item.tolongnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.True(find.tolongnullable); + + item.tolongnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tolongnullable, find.tolongnullable); + Assert.False(find.tolongnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolongnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tolongnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tolongnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tolongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tolongnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tolongnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tolongnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void Byte() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tobyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.True(find.tobyte); + + item = new BoolMap { tobyte = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.False(find.tobyte); + + //update all + item.tobyte = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.True(find.tobyte); + + item.tobyte = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobyte, find.tobyte); + Assert.False(find.tobyte); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobyte, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tobyte); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobyte, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobyte == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tobyte); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobyte == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tobyte == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ByteNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.True(find.tobytenullable); + + item = new BoolMap { tobytenullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.False(find.tobytenullable); + + //update all + item.tobytenullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.True(find.tobytenullable); + + item.tobytenullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tobytenullable, find.tobytenullable); + Assert.False(find.tobytenullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobytenullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tobytenullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tobytenullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tobytenullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tobytenullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tobytenullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tobytenullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UShort() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toushort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.True(find.toushort); + + item = new BoolMap { toushort = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.False(find.toushort); + + //update all + item.toushort = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.True(find.toushort); + + item.toushort = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushort, find.toushort); + Assert.False(find.toushort); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushort, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toushort); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushort, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushort == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toushort); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toushort == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toushort == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UShortNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.True(find.toushortnullable); + + item = new BoolMap { toushortnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.False(find.toushortnullable); + + //update all + item.toushortnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.True(find.toushortnullable); + + item.toushortnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toushortnullable, find.toushortnullable); + Assert.False(find.toushortnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushortnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toushortnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toushortnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toushortnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toushortnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toushortnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toushortnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UInt() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.touint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.True(find.touint); + + item = new BoolMap { touint = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.False(find.touint); + + //update all + item.touint = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.True(find.touint); + + item.touint = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touint, find.touint); + Assert.False(find.touint); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touint, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.touint); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touint, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touint == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.touint); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.touint == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.touint == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void UIntNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.touintnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.True(find.touintnullable); + + item = new BoolMap { touintnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.False(find.touintnullable); + + //update all + item.touintnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.True(find.touintnullable); + + item.touintnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.touintnullable, find.touintnullable); + Assert.False(find.touintnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touintnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.touintnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.touintnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.touintnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.touintnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.touintnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.touintnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ULong() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toulong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.True(find.toulong); + + item = new BoolMap { toulong = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.False(find.toulong); + + //update all + item.toulong = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.True(find.toulong); + + item.toulong = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulong, find.toulong); + Assert.False(find.toulong); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulong, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toulong); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulong, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulong == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toulong); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toulong == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toulong == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void ULongNullable() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.True(find.toulongnullable); + + item = new BoolMap { toulongnullable = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.False(find.toulongnullable); + + //update all + item.toulongnullable = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.True(find.toulongnullable); + + item.toulongnullable = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.toulongnullable, find.toulongnullable); + Assert.False(find.toulongnullable); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulongnullable, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.toulongnullable); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.toulongnullable, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.toulongnullable == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.toulongnullable); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.toulongnullable == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.toulongnullable == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void TimeSpan() + { + } + [Fact] + public void TimeSpanNullable() + { + } + [Fact] + public void DateTime() + { + } + [Fact] + public void DateTimeNullable() + { + } + + [Fact] + public void ByteArray() + { + } + [Fact] + public void String() + { + //insert + var orm = g.shentong; + var item = new BoolMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.tostring == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.True(find.tostring); + + item = new BoolMap { tostring = false }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.False(find.tostring); + + //update all + item.tostring = true; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.True(find.tostring); + + item.tostring = false; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.tostring, find.tostring); + Assert.False(find.tostring); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tostring, true).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == true).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.True(find.tostring); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.tostring, false).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.tostring == false).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.False(find.tostring); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.tostring == true).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.tostring == false).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void Guid() + { + } + [Fact] + public void GuidNullable() + { + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/DateTimeOffSetTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/DateTimeOffSetTest.cs new file mode 100644 index 00000000..88eac7b3 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/DateTimeOffSetTest.cs @@ -0,0 +1,54 @@ +using FreeSql.DataAnnotations; +using System; +using System.Numerics; +using Xunit; + +namespace FreeSql.Tests.ShenTongMapType +{ + public class DateTimeOffSetTest + { + class DateTimeOffSetTestMap + { + public Guid id { get; set; } + + [Column(MapType = typeof(DateTime))] + public DateTimeOffset dtos_to_dt { get; set; } + [Column(MapType = typeof(DateTime))] + public DateTimeOffset? dtosnullable_to_dt { get; set; } + } + [Fact] + public void DateTimeToDateTimeOffSet() + { + //insert + var orm = g.shentong; + 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(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.dtos_to_dt.ToString("g"), find.dtos_to_dt.ToString("g")); + Assert.Equal(item.dtosnullable_to_dt.Value.ToString("g"), find.dtosnullable_to_dt.Value.ToString("g")); + + //update all + item.dtos_to_dt = DateTimeOffset.Now; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.dtos_to_dt.ToString("g"), find.dtos_to_dt.ToString("g")); + Assert.Equal(item.dtosnullable_to_dt.Value.ToString("g"), find.dtosnullable_to_dt.Value.ToString("g")); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.dtos_to_dt, item.dtos_to_dt = DateTimeOffset.Now).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.dtos_to_dt.ToString("g"), find.dtos_to_dt.ToString("g")); + Assert.Equal(item.dtosnullable_to_dt.Value.ToString("g"), find.dtosnullable_to_dt.Value.ToString("g")); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/EnumTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/EnumTest.cs new file mode 100644 index 00000000..8eb15a3f --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/EnumTest.cs @@ -0,0 +1,261 @@ +using FreeSql.DataAnnotations; +using System; +using System.Numerics; +using Xunit; + +namespace FreeSql.Tests.ShenTongMapType +{ + public class EnumTest + { + class EnumTestMap + { + public Guid id { get; set; } + + [Column(MapType = typeof(string))] + public ToStringMapEnum enum_to_string { get; set; } + [Column(MapType = typeof(string))] + public ToStringMapEnum? enumnullable_to_string { get; set; } + + [Column(MapType = typeof(int))] + public ToStringMapEnum enum_to_int { get; set; } + [Column(MapType = typeof(int?))] + public ToStringMapEnum? enumnullable_to_int { get; set; } + } + public enum ToStringMapEnum { й, abc, } + [Fact] + public void EnumToString() + { + //insert + var orm = g.shentong; + var item = new EnumTestMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum.й, find.enum_to_string); + + item = new EnumTestMap { enum_to_string = ToStringMapEnum.abc }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum.abc, find.enum_to_string); + + //update all + item.enum_to_string = ToStringMapEnum.; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum., find.enum_to_string); + + item.enum_to_string = ToStringMapEnum.й; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum.й, find.enum_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enum_to_string, ToStringMapEnum.).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum., find.enum_to_string); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enum_to_string, ToStringMapEnum.abc).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum.abc, find.enum_to_string); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.й).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.abc).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void EnumNullableToString() + { + //insert + var orm = g.shentong; + var item = new EnumTestMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Null(find.enumnullable_to_string); + + item = new EnumTestMap { enumnullable_to_string = ToStringMapEnum.й }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Equal(ToStringMapEnum.й, find.enumnullable_to_string); + + //update all + item.enumnullable_to_string = ToStringMapEnum.; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Equal(ToStringMapEnum., find.enumnullable_to_string); + + item.enumnullable_to_string = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.).First()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Null(find.enumnullable_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enumnullable_to_string, ToStringMapEnum.abc).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum.abc, find.enumnullable_to_string); + + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enumnullable_to_string, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.abc).First()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.enumnullable_to_string); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.й).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_string == null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void EnumToInt() + { + //insert + var orm = g.shentong; + var item = new EnumTestMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_int, find.enum_to_int); + Assert.Equal(ToStringMapEnum.й, find.enum_to_int); + + item = new EnumTestMap { enum_to_int = ToStringMapEnum.abc }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_int, find.enum_to_int); + Assert.Equal(ToStringMapEnum.abc, find.enum_to_int); + + //update all + item.enum_to_int = ToStringMapEnum.; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_int, find.enum_to_int); + Assert.Equal(ToStringMapEnum., find.enum_to_int); + + item.enum_to_int = ToStringMapEnum.й; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_int, find.enum_to_int); + Assert.Equal(ToStringMapEnum.й, find.enum_to_int); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enum_to_int, ToStringMapEnum.).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum., find.enum_to_int); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enum_to_int, ToStringMapEnum.abc).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum.abc, find.enum_to_int); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.й).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.enum_to_int == ToStringMapEnum.abc).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void EnumNullableToInt() + { + //insert + var orm = g.shentong; + var item = new EnumTestMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_int, find.enumnullable_to_int); + Assert.Null(find.enumnullable_to_int); + + item = new EnumTestMap { enumnullable_to_int = ToStringMapEnum.й }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_int, find.enumnullable_to_int); + Assert.Equal(ToStringMapEnum.й, find.enumnullable_to_int); + + //update all + item.enumnullable_to_int = ToStringMapEnum.; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_int, find.enumnullable_to_int); + Assert.Equal(ToStringMapEnum., find.enumnullable_to_int); + + item.enumnullable_to_int = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.).First()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_int, find.enumnullable_to_int); + Assert.Null(find.enumnullable_to_int); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enumnullable_to_int, ToStringMapEnum.abc).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum.abc, find.enumnullable_to_int); + + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enumnullable_to_int, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.abc).First()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_int == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.enumnullable_to_int); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.й).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_int == ToStringMapEnum.).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_int == null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/ToStringTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/ToStringTest.cs new file mode 100644 index 00000000..5b964d94 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/MapType/ToStringTest.cs @@ -0,0 +1,570 @@ +using FreeSql.DataAnnotations; +using System; +using System.Numerics; +using Xunit; + +namespace FreeSql.Tests.ShenTongMapType +{ + public class ToStringTest + { + class ToStringMap + { + public Guid id { get; set; } + + [Column(MapType = typeof(string))] + public TimeSpan timespan_to_string { get; set; } + [Column(MapType = typeof(string))] + public TimeSpan? timespannullable_to_string { get; set; } + + [Column(MapType = typeof(string))] + public DateTime datetime_to_string { get; set; } + [Column(MapType = typeof(string))] + public DateTime? datetimenullable_to_string { get; set; } + + [Column(MapType = typeof(string))] + public Guid guid_to_string { get; set; } + [Column(MapType = typeof(string))] + public Guid? guidnullable_to_string { get; set; } + + [Column(MapType = typeof(string))] + public ToStringMapEnum enum_to_string { get; set; } + [Column(MapType = typeof(string))] + public ToStringMapEnum? enumnullable_to_string { get; set; } + + [Column(MapType = typeof(string))] + public BigInteger biginteger_to_string { get; set; } + [Column(MapType = typeof(string))] + public BigInteger? bigintegernullable_to_string { get; set; } + } + public enum ToStringMapEnum { й, abc, } + [Fact] + public void Enum1() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum.й, find.enum_to_string); + + item = new ToStringMap { enum_to_string = ToStringMapEnum.abc }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum.abc, find.enum_to_string); + + //update all + item.enum_to_string = ToStringMapEnum.; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum., find.enum_to_string); + + item.enum_to_string = ToStringMapEnum.й; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enum_to_string, find.enum_to_string); + Assert.Equal(ToStringMapEnum.й, find.enum_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enum_to_string, ToStringMapEnum.).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum., find.enum_to_string); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enum_to_string, ToStringMapEnum.abc).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum.abc, find.enum_to_string); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.й).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.enum_to_string == ToStringMapEnum.abc).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void EnumNullable() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Null(find.enumnullable_to_string); + + item = new ToStringMap { enumnullable_to_string = ToStringMapEnum.й }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.й).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Equal(ToStringMapEnum.й, find.enumnullable_to_string); + + //update all + item.enumnullable_to_string = ToStringMapEnum.; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Equal(ToStringMapEnum., find.enumnullable_to_string); + + item.enumnullable_to_string = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.).First()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.enumnullable_to_string, find.enumnullable_to_string); + Assert.Null(find.enumnullable_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enumnullable_to_string, ToStringMapEnum.abc).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.abc).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(ToStringMapEnum.abc, find.enumnullable_to_string); + + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.enumnullable_to_string, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.abc).First()); + find = orm.Select().Where(a => a.id == item.id && a.enumnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.enumnullable_to_string); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.й).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_string == ToStringMapEnum.).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.enumnullable_to_string == null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void BigInteger1() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.biginteger_to_string == 0).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.biginteger_to_string, find.biginteger_to_string); + Assert.Equal(0, find.biginteger_to_string); + + item = new ToStringMap { biginteger_to_string = 100 }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.biginteger_to_string == 100).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.biginteger_to_string, find.biginteger_to_string); + Assert.Equal(100, find.biginteger_to_string); + + //update all + item.biginteger_to_string = 200; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.biginteger_to_string == 200).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.biginteger_to_string, find.biginteger_to_string); + Assert.Equal(200, find.biginteger_to_string); + + item.biginteger_to_string = 205; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.biginteger_to_string == 205).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.biginteger_to_string, find.biginteger_to_string); + Assert.Equal(205, find.biginteger_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.biginteger_to_string, 522).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.biginteger_to_string == 522).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(522, find.biginteger_to_string); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.biginteger_to_string, 10005).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.biginteger_to_string == 10005).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(10005, find.biginteger_to_string); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.biginteger_to_string == 522).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.biginteger_to_string == 205).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.biginteger_to_string == 10005).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void BigIntegerNullable() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.bigintegernullable_to_string, find.bigintegernullable_to_string); + Assert.Null(find.bigintegernullable_to_string); + + item = new ToStringMap { bigintegernullable_to_string = 101 }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == 101).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.bigintegernullable_to_string, find.bigintegernullable_to_string); + Assert.Equal(101, find.bigintegernullable_to_string); + + //update all + item.bigintegernullable_to_string = 2004; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == 2004).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.bigintegernullable_to_string, find.bigintegernullable_to_string); + Assert.Equal(2004, find.bigintegernullable_to_string); + + item.bigintegernullable_to_string = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == 2004).First()); + find = orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.bigintegernullable_to_string, find.bigintegernullable_to_string); + Assert.Null(find.bigintegernullable_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.bigintegernullable_to_string, 998).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == 998).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(998, find.bigintegernullable_to_string); + + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.bigintegernullable_to_string, null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == 998).First()); + find = orm.Select().Where(a => a.id == item.id && a.bigintegernullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.bigintegernullable_to_string); + + //delete + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.bigintegernullable_to_string == 998).ExecuteAffrows()); + Assert.Equal(0, orm.Delete().Where(a => a.id == item.id && a.bigintegernullable_to_string == 2004).ExecuteAffrows()); + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.bigintegernullable_to_string == null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void TimeSpan1() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespan_to_string, find.timespan_to_string); + Assert.Equal(TimeSpan.Zero, find.timespan_to_string); + + item = new ToStringMap { timespan_to_string = TimeSpan.FromDays(1) }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespan_to_string, find.timespan_to_string); + Assert.Equal(TimeSpan.FromDays(1), find.timespan_to_string); + + //update all + item.timespan_to_string = TimeSpan.FromHours(10); + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespan_to_string, find.timespan_to_string); + Assert.Equal(TimeSpan.FromHours(10), find.timespan_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.timespan_to_string, TimeSpan.FromHours(11)).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(TimeSpan.FromHours(11), find.timespan_to_string); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void TimeSpanNullable() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespannullable_to_string, find.timespannullable_to_string); + Assert.Null(find.timespannullable_to_string); + + item = new ToStringMap { timespannullable_to_string = TimeSpan.FromDays(1) }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespannullable_to_string, find.timespannullable_to_string); + Assert.Equal(TimeSpan.FromDays(1), find.timespannullable_to_string); + + //update all + item.timespannullable_to_string = TimeSpan.FromHours(10); + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespannullable_to_string, find.timespannullable_to_string); + Assert.Equal(TimeSpan.FromHours(10), find.timespannullable_to_string); + + item.timespannullable_to_string = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.timespannullable_to_string, find.timespannullable_to_string); + Assert.Null(find.timespannullable_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.timespannullable_to_string, TimeSpan.FromHours(11)).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(TimeSpan.FromHours(11), find.timespannullable_to_string); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.timespannullable_to_string, null).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.timespannullable_to_string); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void DateTime1() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetime_to_string, find.datetime_to_string); + Assert.Equal(DateTime.MinValue, find.datetime_to_string); + + item = new ToStringMap { datetime_to_string = DateTime.Parse("2000-1-1") }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetime_to_string, find.datetime_to_string); + Assert.Equal(DateTime.Parse("2000-1-1"), find.datetime_to_string); + + //update all + item.datetime_to_string = DateTime.Parse("2000-1-11"); + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetime_to_string, find.datetime_to_string); + Assert.Equal(DateTime.Parse("2000-1-11"), find.datetime_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.datetime_to_string, DateTime.Parse("2000-1-12")).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(DateTime.Parse("2000-1-12"), find.datetime_to_string); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void DateTimeNullable() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetimenullable_to_string, find.datetimenullable_to_string); + Assert.Null(find.datetimenullable_to_string); + + item = new ToStringMap { datetimenullable_to_string = DateTime.Parse("2000-1-1") }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetimenullable_to_string, find.datetimenullable_to_string); + Assert.Equal(DateTime.Parse("2000-1-1"), find.datetimenullable_to_string); + + //update all + item.datetimenullable_to_string = DateTime.Parse("2000-1-11"); + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetimenullable_to_string, find.datetimenullable_to_string); + Assert.Equal(DateTime.Parse("2000-1-11"), find.datetimenullable_to_string); + + item.datetimenullable_to_string = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.datetimenullable_to_string, find.datetimenullable_to_string); + Assert.Null(find.datetimenullable_to_string); + + //update set + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.datetimenullable_to_string, DateTime.Parse("2000-1-12")).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(DateTime.Parse("2000-1-12"), find.datetimenullable_to_string); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.datetimenullable_to_string, null).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.datetimenullable_to_string); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + + [Fact] + public void Guid1() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.guid_to_string == Guid.Empty).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guid_to_string, find.guid_to_string); + Assert.Equal(Guid.Empty, find.guid_to_string); + + var newid = Guid.NewGuid(); + item = new ToStringMap { guid_to_string = newid }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guid_to_string == newid).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guid_to_string, find.guid_to_string); + Assert.Equal(newid, find.guid_to_string); + + //update all + newid = Guid.NewGuid(); + item.guid_to_string = newid; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guid_to_string == newid).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guid_to_string, find.guid_to_string); + Assert.Equal(newid, find.guid_to_string); + + //update set + newid = Guid.NewGuid(); + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.guid_to_string, newid).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guid_to_string == newid).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(newid, find.guid_to_string); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.guid_to_string == newid).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + [Fact] + public void GuidNullable() + { + //insert + var orm = g.shentong; + var item = new ToStringMap { }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + var find = orm.Select().Where(a => a.id == item.id && a.guidnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guidnullable_to_string, find.guidnullable_to_string); + Assert.Null(find.guidnullable_to_string); + + var newid = Guid.NewGuid(); + item = new ToStringMap { guidnullable_to_string = newid }; + Assert.Equal(1, orm.Insert().AppendData(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guidnullable_to_string == newid).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guidnullable_to_string, find.guidnullable_to_string); + Assert.Equal(newid, find.guidnullable_to_string); + + //update all + newid = Guid.NewGuid(); + item.guidnullable_to_string = newid; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guidnullable_to_string == newid).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guidnullable_to_string, find.guidnullable_to_string); + Assert.Equal(newid, find.guidnullable_to_string); + + item.guidnullable_to_string = null; + Assert.Equal(1, orm.Update().SetSource(item).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guidnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(item.guidnullable_to_string, find.guidnullable_to_string); + Assert.Null(find.guidnullable_to_string); + + //update set + newid = Guid.NewGuid(); + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.guidnullable_to_string, newid).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guidnullable_to_string == newid).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Equal(newid, find.guidnullable_to_string); + + Assert.Equal(1, orm.Update().Where(a => a.id == item.id).Set(a => a.guidnullable_to_string, null).ExecuteAffrows()); + find = orm.Select().Where(a => a.id == item.id && a.guidnullable_to_string == null).First(); + Assert.NotNull(find); + Assert.Equal(item.id, find.id); + Assert.Null(find.guidnullable_to_string); + + //delete + Assert.Equal(1, orm.Delete().Where(a => a.id == item.id && a.guidnullable_to_string == null).ExecuteAffrows()); + Assert.Null(orm.Select().Where(a => a.id == item.id).First()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAdo/ShenTongAdoTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAdo/ShenTongAdoTest.cs new file mode 100644 index 00000000..84daa489 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAdo/ShenTongAdoTest.cs @@ -0,0 +1,68 @@ +using FreeSql.DataAnnotations; +using System; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongAdoTest + { + [Fact] + public void Pool() + { + var t1 = g.shentong.Ado.MasterPool.StatisticsFullily; + } + + [Fact] + public void SlavePools() + { + var t2 = g.shentong.Ado.SlavePools.Count; + } + + [Fact] + public void ExecuteReader() + { + + } + [Fact] + public void ExecuteArray() + { + + } + [Fact] + public void ExecuteNonQuery() + { + + } + [Fact] + public void ExecuteScalar() + { + + } + + [Fact] + public void Query() + { + + g.shentong.CodeFirst.SyncStructure(); + var t3 = g.shentong.Ado.Query("select * from xxx"); + + var t4 = g.shentong.Ado.Query<(int, string, string)>("select * from xxx"); + + var t5 = g.shentong.Ado.Query("select * from xxx"); + } + + [Fact] + public void QueryMultipline() + { + g.shentong.CodeFirst.SyncStructure(); + var t3 = g.shentong.Ado.Query("select * from xxx; select * from xxx; select * from xxx"); + } + + class xxx + { + public string Id { get; set; } + public string Path { get; set; } + public string Title2 { get; set; } + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAopTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAopTest.cs new file mode 100644 index 00000000..f737d867 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongAopTest.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongAopTest + { + + class TestAuditValue + { + public Guid id { get; set; } + [Now] + public DateTime createtime { get; set; } + } + class NowAttribute: Attribute { } + + [Fact] + public void AuditValue() + { + var date = DateTime.Now.Date; + var item = new TestAuditValue(); + + EventHandler audit = (s, e) => + { + if (e.Property.GetCustomAttribute(false) != null) + e.Value = DateTime.Now.Date; + }; + g.shentong.Aop.AuditValue += audit; + + g.shentong.Insert(item).ExecuteAffrows(); + + g.shentong.Aop.AuditValue -= audit; + + Assert.Equal(item.createtime, date); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongCodeFirstTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongCodeFirstTest.cs new file mode 100644 index 00000000..23475c9f --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongCodeFirstTest.cs @@ -0,0 +1,348 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Text; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongCodeFirstTest + { + [Fact] + public void StringLength() + { + var dll = g.shentong.CodeFirst.GetComparisonDDLStatements(); + g.shentong.CodeFirst.SyncStructure(); + } + class TS_SLTB + { + public Guid Id { get; set; } + [Column(StringLength = 50)] + public string Title { get; set; } + + [Column(IsNullable = false, StringLength = 50)] + public string TitleSub { get; set; } + } + + [Fact] + public void ı_ֶ() + { + var sql = g.shentong.CodeFirst.GetComparisonDDLStatements<ı>(); + g.shentong.CodeFirst.SyncStructure<ı>(); + + var item = new ı + { + = "Ա", + ʱ = DateTime.Now + }; + Assert.Equal(1, g.shentong.Insert<ı>().NoneParameter().AppendData(item).ExecuteAffrows()); + Assert.NotEqual(Guid.Empty, item.); + var item2 = g.shentong.Select<ı>().Where(a => a. == item.).First(); + Assert.NotNull(item2); + Assert.Equal(item., item2.); + Assert.Equal(item., item2.); + + item. = "Ա"; + Assert.Equal(1, g.shentong.Update<ı>().NoneParameter().SetSource(item).ExecuteAffrows()); + item2 = g.shentong.Select<ı>().Where(a => a. == item.).First(); + Assert.NotNull(item2); + Assert.Equal(item., item2.); + Assert.Equal(item., item2.); + + item. = "Ա_repo"; + var repo = g.shentong.GetRepository<ı>(); + repo.DbContextOptions.NoneParameter = true; + Assert.Equal(1, repo.Update(item)); + item2 = g.shentong.Select<ı>().Where(a => a. == item.).First(); + Assert.NotNull(item2); + Assert.Equal(item., item2.); + Assert.Equal(item., item2.); + + item. = "Ա_repo22"; + Assert.Equal(1, repo.Update(item)); + item2 = g.shentong.Select<ı>().Where(a => a. == item.).First(); + Assert.NotNull(item2); + Assert.Equal(item., item2.); + Assert.Equal(item., item2.); + } + class ı + { + [Column(IsPrimary = true)] + public Guid { get; set; } + + public string { get; set; } + + [Column(ServerTime = DateTimeKind.Local, CanUpdate = false)] + public DateTime ʱ { get; set; } + + [Column(ServerTime = DateTimeKind.Local)] + public DateTime ʱ { get; set; } + } + + [Fact] + public void AddUniques() + { + var sql = g.shentong.CodeFirst.GetComparisonDDLStatements(); + g.shentong.CodeFirst.SyncStructure(); + } + [Table(Name = "AddUniquesInfo", OldName = "AddUniquesInfo2")] + [Index("uk_phone", "phone", true)] + [Index("uk_group_index", "group,index", true)] + [Index("uk_group_index22", "group, index22", false)] + class AddUniquesInfo + { + public Guid id { get; set; } + public string phone { get; set; } + + public string group { get; set; } + public int index { get; set; } + public string index22 { get; set; } + } + + [Fact] + public void AddField() + { + var sql = g.shentong.CodeFirst.GetComparisonDDLStatements(); + g.shentong.Select(); + + var id = g.shentong.Insert().AppendData(new TopicAddField { }).ExecuteIdentity(); + } + + [Table(Name = "ccc.TopicAddField", OldName = "TopicAddField")] + public class TopicAddField + { + [Column(IsIdentity = true)] + public int Id { get; set; } + + public string name { get; set; } = "xxx"; + + public int clicks { get; set; } = 10; + //public int name { get; set; } = 3000; + + //[Column(DbType = "varchar(200) not null", OldName = "title")] + //public string title222 { get; set; } = "333"; + + //[Column(DbType = "varchar(200) not null")] + //public string title222333 { get; set; } = "xxx"; + + //[Column(DbType = "varchar(100) not null", OldName = "title122333aaa")] + //public string titleaaa { get; set; } = "fsdf"; + + + [Column(IsIgnore = true)] + public DateTime ct { get; set; } = DateTime.Now; + } + + [Fact] + public void GetComparisonDDLStatements() + { + + var sql = g.shentong.CodeFirst.GetComparisonDDLStatements(); + g.shentong.Select(); + } + + IInsert insert => g.shentong.Insert(); + ISelect select => g.shentong.Select(); + + [Fact] + public void CurdAllField() + { + //var sql1 = select.Where(a => a.testFieldIntArray.Contains(1)).ToSql(); + //var sql2 = select.Where(a => a.testFieldIntArray.Contains(1)).ToSql(); + + var item = new TableAllType { }; + item.Id = (int)insert.AppendData(item).ExecuteIdentity(); + + var newitem = select.Where(a => a.Id == item.Id).ToOne(); + + var item2 = new TableAllType + { + testFieldBool = true, + //testFieldBoolArray = new[] { true, true, false, false }, + //testFieldBoolArrayNullable = new bool?[] { true, true, null, false, false }, + testFieldBoolNullable = true, + testFieldByte = byte.MaxValue, + //testFieldByteArray = new byte[] { 0, 1, 2, 3, 4, 5, 6 }, + //testFieldByteArrayNullable = new byte?[] { 0, 1, 2, 3, null, 4, 5, 6 }, + testFieldByteNullable = byte.MinValue, + testFieldBytes = Encoding.UTF8.GetBytes("й"), + //testFieldBytesArray = new[] { Encoding.UTF8.GetBytes("й"), Encoding.UTF8.GetBytes("й") }, + testFieldDateTime = DateTime.Now, + //testFieldDateTimeArray = new[] { DateTime.Now, DateTime.Now.AddHours(2) }, + //testFieldDateTimeArrayNullable = new DateTime?[] { DateTime.Now, null, DateTime.Now.AddHours(2) }, + testFieldDateTimeNullable = DateTime.Now.AddDays(-1), + testFieldDecimal = 999.99M, + //testFieldDecimalArray = new[] { 999.91M, 999.92M, 999.93M }, + //testFieldDecimalArrayNullable = new decimal?[] { 998.11M, 998.12M, 998.13M }, + testFieldDecimalNullable = 111.11M, + testFieldDouble = 888.88, + //testFieldDoubleArray = new[] { 888.81, 888.82, 888.83 }, + //testFieldDoubleArrayNullable = new double?[] { 888.11, 888.12, null, 888.13 }, + testFieldDoubleNullable = 222.22, + testFieldEnum1 = TableAllTypeEnumType1.e3, + //testFieldEnum1Array = new[] { TableAllTypeEnumType1.e5, TableAllTypeEnumType1.e2, TableAllTypeEnumType1.e1 }, + //testFieldEnum1ArrayNullable = new TableAllTypeEnumType1?[] { TableAllTypeEnumType1.e5, TableAllTypeEnumType1.e2, null, TableAllTypeEnumType1.e1 }, + testFieldEnum1Nullable = TableAllTypeEnumType1.e2, + testFieldEnum2 = TableAllTypeEnumType2.f2, + //testFieldEnum2Array = new[] { TableAllTypeEnumType2.f3, TableAllTypeEnumType2.f1 }, + //testFieldEnum2ArrayNullable = new TableAllTypeEnumType2?[] { TableAllTypeEnumType2.f3, null, TableAllTypeEnumType2.f1 }, + testFieldEnum2Nullable = TableAllTypeEnumType2.f3, + testFieldFloat = 777.77F, + //testFieldFloatArray = new[] { 777.71F, 777.72F, 777.73F }, + //testFieldFloatArrayNullable = new float?[] { 777.71F, 777.72F, null, 777.73F }, + testFieldFloatNullable = 333.33F, + testFieldGuid = Guid.NewGuid(), + //testFieldGuidArray = new[] { Guid.NewGuid(), Guid.NewGuid() }, + //testFieldGuidArrayNullable = new Guid?[] { Guid.NewGuid(), null, Guid.NewGuid() }, + testFieldGuidNullable = Guid.NewGuid(), + testFieldInt = int.MaxValue, + //testFieldIntArray = new[] { 1, 2, 3, 4, 5 }, + //testFieldIntArrayNullable = new int?[] { 1, 2, 3, null, 4, 5 }, + testFieldIntNullable = int.MinValue, + testFieldLong = long.MaxValue, + //testFieldLongArray = new long[] { 10, 20, 30, 40, 50 }, + testFieldSByte = sbyte.MaxValue, + //testFieldSByteArray = new sbyte[] { 1, 2, 3, 4, 5 }, + //testFieldSByteArrayNullable = new sbyte?[] { 1, 2, 3, null, 4, 5 }, + testFieldSByteNullable = sbyte.MinValue, + testFieldShort = short.MaxValue, + //testFieldShortArray = new short[] { 1, 2, 3, 4, 5 }, + //testFieldShortArrayNullable = new short?[] { 1, 2, 3, null, 4, 5 }, + testFieldShortNullable = short.MinValue, + testFieldString = "йstring'\\?!@#$%^&*()_+{}}{~?><<>", + //testFieldStringArray = new[] { "йString1", "йString2", null, "йString3" }, + testFieldTimeSpan = TimeSpan.FromHours(10), + //testFieldTimeSpanArray = new[] { TimeSpan.FromHours(10), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(60) }, + //testFieldTimeSpanArrayNullable = new TimeSpan?[] { TimeSpan.FromHours(10), TimeSpan.FromSeconds(10), null, TimeSpan.FromSeconds(60) }, + testFieldTimeSpanNullable = TimeSpan.FromSeconds(90), + testFieldUInt = uint.MaxValue, + //testFieldUIntArray = new uint[] { 1, 2, 3, 4, 5 }, + //testFieldUIntArrayNullable = new uint?[] { 1, 2, 3, null, 4, 5 }, + testFieldUIntNullable = uint.MinValue, + testFieldULong = ulong.MaxValue, + //testFieldULongArray = new ulong[] { 10, 20, 30, 40, 50 }, + //testFieldULongArrayNullable = new ulong?[] { 10, 20, 30, null, 40, 50 }, + testFieldULongNullable = ulong.MinValue, + testFieldUShort = ushort.MaxValue, + //testFieldUShortArray = new ushort[] { 11, 12, 13, 14, 15 }, + //testFieldUShortArrayNullable = new ushort?[] { 11, 12, 13, null, 14, 15 }, + testFieldUShortNullable = ushort.MinValue, + //testFielLongArrayNullable = new long?[] { 500, 600, 700, null, 999, 1000 }, + testFielLongNullable = long.MinValue + }; + + var sqlPar = insert.AppendData(item2).ToSql(); + var sqlText = insert.AppendData(item2).NoneParameter().ToSql(); + var item3NP = insert.AppendData(item2).NoneParameter().ExecuteInserted(); + + var item3 = insert.AppendData(item2).ExecuteInserted().First(); + var newitem2 = select.Where(a => a.Id == item3.Id).ToOne(); + Assert.Equal(item2.testFieldString, newitem2.testFieldString); + + item3 = insert.NoneParameter().AppendData(item2).ExecuteInserted().First(); + newitem2 = select.Where(a => a.Id == item3.Id).ToOne(); + Assert.Equal(item2.testFieldString, newitem2.testFieldString); + + var items = select.ToList(); + } + + [Table(Name = "tb_alltype")] + class TableAllType + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + + public bool testFieldBool { get; set; } + public sbyte testFieldSByte { get; set; } + public short testFieldShort { get; set; } + public int testFieldInt { get; set; } + public long testFieldLong { get; set; } + public byte testFieldByte { get; set; } + public ushort testFieldUShort { get; set; } + public uint testFieldUInt { get; set; } + public ulong testFieldULong { get; set; } + public double testFieldDouble { get; set; } + public float testFieldFloat { get; set; } + public decimal testFieldDecimal { get; set; } + public TimeSpan testFieldTimeSpan { get; set; } + + [Column(ServerTime = DateTimeKind.Local)] + public DateTime testFieldDateTime { get; set; } + + public byte[] testFieldBytes { get; set; } + public string testFieldString { get; set; } + public Guid testFieldGuid { get; set; } + + public bool? testFieldBoolNullable { get; set; } + public sbyte? testFieldSByteNullable { get; set; } + public short? testFieldShortNullable { get; set; } + public int? testFieldIntNullable { get; set; } + public long? testFielLongNullable { get; set; } + public byte? testFieldByteNullable { get; set; } + public ushort? testFieldUShortNullable { get; set; } + public uint? testFieldUIntNullable { get; set; } + public ulong? testFieldULongNullable { get; set; } + public double? testFieldDoubleNullable { get; set; } + public float? testFieldFloatNullable { get; set; } + public decimal? testFieldDecimalNullable { get; set; } + public TimeSpan? testFieldTimeSpanNullable { get; set; } + + [Column(ServerTime = DateTimeKind.Local)] + public DateTime? testFieldDateTimeNullable { get; set; } + + public Guid? testFieldGuidNullable { get; set; } + + public TableAllTypeEnumType1 testFieldEnum1 { get; set; } + public TableAllTypeEnumType1? testFieldEnum1Nullable { get; set; } + public TableAllTypeEnumType2 testFieldEnum2 { get; set; } + public TableAllTypeEnumType2? testFieldEnum2Nullable { get; set; } + + /* array */ + //public bool[] testFieldBoolArray { get; set; } + //public sbyte[] testFieldSByteArray { get; set; } + //public short[] testFieldShortArray { get; set; } + //public int[] testFieldIntArray { get; set; } + //public long[] testFieldLongArray { get; set; } + //public byte[] testFieldByteArray { get; set; } + //public ushort[] testFieldUShortArray { get; set; } + //public uint[] testFieldUIntArray { get; set; } + //public ulong[] testFieldULongArray { get; set; } + //public double[] testFieldDoubleArray { get; set; } + //public float[] testFieldFloatArray { get; set; } + //public decimal[] testFieldDecimalArray { get; set; } + //public TimeSpan[] testFieldTimeSpanArray { get; set; } + //public DateTime[] testFieldDateTimeArray { get; set; } + //public byte[][] testFieldBytesArray { get; set; } + //public string[] testFieldStringArray { get; set; } + //public Guid[] testFieldGuidArray { get; set; } + + //public bool?[] testFieldBoolArrayNullable { get; set; } + //public sbyte?[] testFieldSByteArrayNullable { get; set; } + //public short?[] testFieldShortArrayNullable { get; set; } + //public int?[] testFieldIntArrayNullable { get; set; } + //public long?[] testFielLongArrayNullable { get; set; } + //public byte?[] testFieldByteArrayNullable { get; set; } + //public ushort?[] testFieldUShortArrayNullable { get; set; } + //public uint?[] testFieldUIntArrayNullable { get; set; } + //public ulong?[] testFieldULongArrayNullable { get; set; } + //public double?[] testFieldDoubleArrayNullable { get; set; } + //public float?[] testFieldFloatArrayNullable { get; set; } + //public decimal?[] testFieldDecimalArrayNullable { get; set; } + //public TimeSpan?[] testFieldTimeSpanArrayNullable { get; set; } + //public DateTime?[] testFieldDateTimeArrayNullable { get; set; } + //public Guid?[] testFieldGuidArrayNullable { get; set; } + + //public TableAllTypeEnumType1[] testFieldEnum1Array { get; set; } + //public TableAllTypeEnumType1?[] testFieldEnum1ArrayNullable { get; set; } + //public TableAllTypeEnumType2[] testFieldEnum2Array { get; set; } + //public TableAllTypeEnumType2?[] testFieldEnum2ArrayNullable { get; set; } + } + + public enum TableAllTypeEnumType1 { e1, e2, e3, e5 } + [Flags] public enum TableAllTypeEnumType2 { f1, f2, f3 } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongDbFirstTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongDbFirstTest.cs new file mode 100644 index 00000000..e331b44a --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongDbFirstTest.cs @@ -0,0 +1,25 @@ +using FreeSql.DataAnnotations; +using System; +using Xunit; + +namespace FreeSql.Tests.ShenTong +{ + public class ShenTongDbFirstTest + { + [Fact] + public void GetDatabases() + { + + var t1 = g.shentong.DbFirst.GetDatabases(); + + } + + [Fact] + public void GetTablesByDatabase() + { + + var t2 = g.shentong.DbFirst.GetTablesByDatabase(g.shentong.DbFirst.GetDatabases()[1]); + + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/ConvertTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/ConvertTest.cs new file mode 100644 index 00000000..a0debb15 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/ConvertTest.cs @@ -0,0 +1,169 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTongExpression +{ + public class ConvertTest + { + + ISelect select => g.shentong.Select(); + + [Table(Name = "tb_topic")] + class Topic + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public int TypeGuid { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + class TestTypeInfo + { + public int Guid { get; set; } + public int ParentId { get; set; } + public TestTypeParentInfo Parent { get; set; } + public string Name { get; set; } + } + class TestTypeParentInfo + { + public int Id { get; set; } + public string Name { get; set; } + + public List Types { get; set; } + } + + [Fact] + public void ToBoolean() + { + var data = new List(); + data.Add(select.Where(a => (Convert.ToBoolean(a.Clicks) ? 1 : 0) > 0).ToList()); + data.Add(select.Where(a => (bool.Parse(a.Clicks.ToString()) ? 1 : 0) > 0).ToList()); + } + [Fact] + public void ToByte() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToByte(a.Clicks % 255) > 0).ToList()); + data.Add(select.Where(a => byte.Parse((a.Clicks % 255).ToString()) > 0).ToList()); + } + [Fact] + public void ToChar() + { + var data = new List(); + //data.Add(select.Where(a => Convert.ToChar(a.Clicks) == '1').ToList()); + //data.Add(select.Where(a => char.Parse(a.Clicks.ToString()) == '1').ToList()); + } + [Fact] + public void ToDateTime() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToDateTime(a.CreateTime.ToString()).Year > 0).ToList()); + data.Add(select.Where(a => DateTime.Parse(a.CreateTime.ToString()).Year > 0).ToList()); + } + [Fact] + public void ToDecimal() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToDecimal(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => decimal.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToDouble() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToDouble(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => double.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToInt16() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToInt16(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => short.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToInt32() + { + var data = new List(); + data.Add(select.Where(a => (int)a.Clicks > 0).ToList()); + data.Add(select.Where(a => Convert.ToInt32(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => int.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToInt64() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToInt64(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => long.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToSByte() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToSByte(a.Clicks % 128) > 0).ToList()); + data.Add(select.Where(a => sbyte.Parse((a.Clicks % 128).ToString()) > 0).ToList()); + } + [Fact] + public void ToSingle() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToSingle(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => float.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void this_ToString() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToString(a.Clicks).Equals("")).ToList()); + data.Add(select.Where(a => a.Clicks.ToString().Equals("")).ToList()); + } + [Fact] + public void ToUInt16() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToUInt16(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => ushort.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToUInt32() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToUInt32(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => uint.Parse(a.Clicks.ToString()) > 0).ToList()); + } + [Fact] + public void ToUInt64() + { + var data = new List(); + data.Add(select.Where(a => Convert.ToUInt64(a.Clicks) > 0).ToList()); + data.Add(select.Where(a => ulong.Parse(a.Clicks.ToString()) > 0).ToList()); + } + + [Fact] + public void Guid_Parse() + { + var data = new List(); + data.Add(select.Where(a => Guid.Parse(Guid.Empty.ToString()) == Guid.Empty).ToList()); + } + + [Fact] + public void Guid_NewGuid() + { + var data = new List(); + //data.Add(select.OrderBy(a => Guid.NewGuid()).Limit(10).ToList()); + } + + [Fact] + public void Random() + { + var data = new List(); + data.Add(select.Where(a => new Random().Next() > a.Clicks).Limit(10).ToList()); + data.Add(select.Where(a => new Random().NextDouble() > a.Clicks).Limit(10).ToList()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/DateTimeTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/DateTimeTest.cs new file mode 100644 index 00000000..a9aa76cf --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/DateTimeTest.cs @@ -0,0 +1,348 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTongExpression +{ + public class DateTimeTest + { + + ISelect select => g.shentong.Select(); + + [Table(Name = "tb_topic111333")] + class Topic + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public int TypeGuid { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + } + [Table(Name = "TestTypeInfo333")] + class TestTypeInfo + { + [Column(IsIdentity = true)] + public int Guid { get; set; } + public int ParentId { get; set; } + public TestTypeParentInfo Parent { get; set; } + public string Name { get; set; } + public DateTime Time { get; set; } = DateTime.Now; + } + [Table(Name = "TestTypeParentInfo23123")] + class TestTypeParentInfo + { + public int Id { get; set; } + public string Name { get; set; } + + public List Types { get; set; } + public DateTime Time2 { get; set; } = DateTime.Now; + } + + [Fact] + public void this_ToString() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.ToString().Equals(DateTime.Now)).ToList()); + data.Add(select.Where(a => a.Type.Time.AddYears(1).ToString().Equals(DateTime.Now)).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddYears(1).ToString().Equals(DateTime.Now)).ToList()); + + g.shentong.Insert(new Topic()).ExecuteAffrows(); + var dtn = DateTime.Parse("2020-1-1 0:0:0"); + var dts = Enumerable.Range(1, 12).Select(a => dtn.AddMonths(a)) + .Concat(Enumerable.Range(1, 31).Select(a => dtn.AddDays(a))) + .Concat(Enumerable.Range(1, 24).Select(a => dtn.AddHours(a))) + .Concat(Enumerable.Range(1, 60).Select(a => dtn.AddMinutes(a))) + .Concat(Enumerable.Range(1, 60).Select(a => dtn.AddSeconds(a))); + foreach (var dt in dts) + { + Assert.Equal(dt.ToString("yyyy-MM-dd HH:mm:ss.ffffff"), select.First(a => dt.ToString())); + Assert.Equal(dt.ToString("yyyy-MM-dd HH:mm:ss"), select.First(a => dt.ToString("yyyy-MM-dd HH:mm:ss"))); + Assert.Equal(dt.ToString("yyyy-MM-dd HH:mm"), select.First(a => dt.ToString("yyyy-MM-dd HH:mm"))); + Assert.Equal(dt.ToString("yyyy-MM-dd HH"), select.First(a => dt.ToString("yyyy-MM-dd HH"))); + Assert.Equal(dt.ToString("yyyy-MM-dd"), select.First(a => dt.ToString("yyyy-MM-dd"))); + Assert.Equal(dt.ToString("yyyy-MM"), select.First(a => dt.ToString("yyyy-MM"))); + Assert.Equal(dt.ToString("yyyyMMddHHmmss"), select.First(a => dt.ToString("yyyyMMddHHmmss"))); + Assert.Equal(dt.ToString("yyyyMMddHHmm"), select.First(a => dt.ToString("yyyyMMddHHmm"))); + Assert.Equal(dt.ToString("yyyyMMddHH"), select.First(a => dt.ToString("yyyyMMddHH"))); + Assert.Equal(dt.ToString("yyyyMMdd"), select.First(a => dt.ToString("yyyyMMdd"))); + Assert.Equal(dt.ToString("yyyyMM"), select.First(a => dt.ToString("yyyyMM"))); + Assert.Equal(dt.ToString("yyyy"), select.First(a => dt.ToString("yyyy"))); + Assert.Equal(dt.ToString("HH:mm:ss"), select.First(a => dt.ToString("HH:mm:ss"))); + Assert.Equal(dt.ToString("yyyy MM dd HH mm ss yy M d H hh h"), select.First(a => dt.ToString("yyyy MM dd HH mm ss yy M d H hh h"))); + Assert.Equal(dt.ToString("yyyy MM dd HH mm ss yy M d H hh h m s tt t").Replace("", "AM").Replace("", "PM").Replace("", "A").Replace("", "P"), select.First(a => dt.ToString("yyyy MM dd HH mm ss yy M d H hh h m s tt t"))); + } + } + [Fact] + public void Now() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Date == DateTime.Now.Date).ToList()); + } + [Fact] + public void UtcNow() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Date == DateTime.UtcNow.Date).ToList()); + } + [Fact] + public void MinValue() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Date == DateTime.MinValue.Date).ToList()); + } + [Fact] + public void MaxValue() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Date == DateTime.MaxValue.Date).ToList()); + } + [Fact] + public void Date() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Date == DateTime.Now.Date).ToList()); + data.Add(select.Where(a => a.Type.Time.Date > DateTime.Now.Date).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Date > DateTime.Now.Date).ToList()); + + data.Add(select.Where(a => DateTime.Now.Subtract(a.CreateTime.Date).TotalSeconds > 0).ToList()); + data.Add(select.Where(a => DateTime.Now.Subtract(a.Type.Time.Date).TotalSeconds > 0).ToList()); + data.Add(select.Where(a => DateTime.Now.Subtract(a.Type.Parent.Time2.Date).TotalSeconds > 0).ToList()); + } + [Fact] + public void TimeOfDay() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay == DateTime.Now.TimeOfDay).ToList()); + data.Add(select.Where(a => a.Type.Time.TimeOfDay > DateTime.Now.TimeOfDay).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.TimeOfDay > DateTime.Now.TimeOfDay).ToList()); + } + [Fact] + public void DayOfWeek() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.DayOfWeek > DateTime.Now.DayOfWeek).ToList()); + data.Add(select.Where(a => a.Type.Time.DayOfWeek > DateTime.Now.DayOfWeek).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.DayOfWeek > DateTime.Now.DayOfWeek).ToList()); + } + [Fact] + public void Day() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Day > DateTime.Now.Day).ToList()); + data.Add(select.Where(a => a.Type.Time.Day > DateTime.Now.Day).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Day > DateTime.Now.Day).ToList()); + } + [Fact] + public void DayOfYear() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.DayOfYear > DateTime.Now.DayOfYear).ToList()); + data.Add(select.Where(a => a.Type.Time.DayOfYear > DateTime.Now.DayOfYear).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.DayOfYear > DateTime.Now.DayOfYear).ToList()); + } + [Fact] + public void Month() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Month > DateTime.Now.Month).ToList()); + data.Add(select.Where(a => a.Type.Time.Month > DateTime.Now.Month).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Month > DateTime.Now.Month).ToList()); + } + [Fact] + public void Year() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Year > DateTime.Now.Year).ToList()); + data.Add(select.Where(a => a.Type.Time.Year > DateTime.Now.Year).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Year > DateTime.Now.Year).ToList()); + } + [Fact] + public void Hour() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Hour > DateTime.Now.Hour).ToList()); + data.Add(select.Where(a => a.Type.Time.Hour > DateTime.Now.Hour).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Hour > DateTime.Now.Hour).ToList()); + } + [Fact] + public void Minute() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Minute > DateTime.Now.Minute).ToList()); + data.Add(select.Where(a => a.Type.Time.Minute > DateTime.Now.Minute).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Minute > DateTime.Now.Minute).ToList()); + } + [Fact] + public void Second() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Second > DateTime.Now.Second).ToList()); + data.Add(select.Where(a => a.Type.Time.Second > DateTime.Now.Second).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Second > DateTime.Now.Second).ToList()); + } + [Fact] + public void Millisecond() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Millisecond > DateTime.Now.Millisecond).ToList()); + data.Add(select.Where(a => a.Type.Time.Millisecond > DateTime.Now.Millisecond).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Millisecond > DateTime.Now.Millisecond).ToList()); + } + [Fact] + public void Ticks() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Ticks > DateTime.Now.Ticks).ToList()); + data.Add(select.Where(a => a.Type.Time.Ticks > DateTime.Now.Ticks).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Ticks > DateTime.Now.Ticks).ToList()); + } + [Fact] + public void Add() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Add(TimeSpan.FromDays(1)) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.Add(TimeSpan.FromDays(1)) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Add(TimeSpan.FromDays(1)) > DateTime.Now).ToList()); + } + [Fact] + public void AddDays() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddDays(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddDays(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddDays(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddHours() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddHours(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddHours(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddHours(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddMilliseconds() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddMilliseconds(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddMilliseconds(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddMilliseconds(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddMinutes() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddMinutes(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddMinutes(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddMinutes(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddMonths() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddMonths(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddMonths(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddMonths(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddSeconds() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddSeconds(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddSeconds(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddSeconds(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddTicks() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddTicks(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddTicks(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddTicks(1) > DateTime.Now).ToList()); + } + [Fact] + public void AddYears() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddYears(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Time.AddYears(1) > DateTime.Now).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddYears(1) > DateTime.Now).ToList()); + } + [Fact] + public void Subtract() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.Subtract(DateTime.Now).TotalSeconds > 0).ToList()); + data.Add(select.Where(a => a.Type.Time.Subtract(DateTime.Now).TotalSeconds > 0).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Subtract(DateTime.Now).TotalSeconds > 0).ToList()); + + data.Add(select.Where(a => a.CreateTime.Subtract(TimeSpan.FromDays(1)) > a.CreateTime).ToList()); + data.Add(select.Where(a => a.Type.Time.Subtract(TimeSpan.FromDays(1)) > a.CreateTime).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.Subtract(TimeSpan.FromDays(1)) > a.CreateTime).ToList()); + } + [Fact] + public void _ЧͬSubtract() + { + var data = new List(); + data.Add(select.Where(a => (a.CreateTime - DateTime.Now).TotalSeconds > 0).ToList()); + data.Add(select.Where(a => (a.Type.Time - DateTime.Now).TotalSeconds > 0).ToList()); + data.Add(select.Where(a => (a.Type.Parent.Time2 - DateTime.Now).TotalSeconds > 0).ToList()); + + data.Add(select.Where(a => (a.CreateTime - TimeSpan.FromDays(1)) > a.CreateTime).ToList()); + data.Add(select.Where(a => (a.Type.Time - TimeSpan.FromDays(1)) > a.CreateTime).ToList()); + data.Add(select.Where(a => (a.Type.Parent.Time2 - TimeSpan.FromDays(1)) > a.CreateTime).ToList()); + } + [Fact] + public void this_Equals() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.AddYears(1).Equals(DateTime.Now)).ToList()); + data.Add(select.Where(a => a.Type.Time.AddYears(1).Equals(DateTime.Now)).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddYears(1).Equals(DateTime.Now)).ToList()); + } + [Fact] + public void DateTime_Compare() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.CompareTo(DateTime.Now) == 0).ToList()); + data.Add(select.Where(a => a.Type.Time.AddYears(1).CompareTo(DateTime.Now) == 0).ToList()); + data.Add(select.Where(a => a.Type.Parent.Time2.AddYears(1).CompareTo(DateTime.Now) == 0).ToList()); + } + [Fact] + public void DateTime_DaysInMonth() + { + var data = new List(); + data.Add(select.Where(a => DateTime.DaysInMonth(a.CreateTime.Year, a.CreateTime.Month) > 30).ToList()); + data.Add(select.Where(a => DateTime.DaysInMonth(a.Type.Time.Year, a.Type.Time.Month) > 30).ToList()); + data.Add(select.Where(a => DateTime.DaysInMonth(a.Type.Parent.Time2.Year, a.Type.Parent.Time2.Month) > 30).ToList()); + } + [Fact] + public void DateTime_Equals() + { + var data = new List(); + data.Add(select.Where(a => DateTime.Equals(a.CreateTime.AddYears(1), DateTime.Now)).ToList()); + data.Add(select.Where(a => DateTime.Equals(a.Type.Time.AddYears(1), DateTime.Now)).ToList()); + data.Add(select.Where(a => DateTime.Equals(a.Type.Parent.Time2.AddYears(1), DateTime.Now)).ToList()); + } + [Fact] + public void DateTime_IsLeapYear() + { + var data = new List(); + data.Add(select.Where(a => DateTime.IsLeapYear(a.CreateTime.Year)).ToList()); + data.Add(select.Where(a => DateTime.IsLeapYear(a.Type.Time.AddYears(1).Year)).ToList()); + data.Add(select.Where(a => DateTime.IsLeapYear(a.Type.Parent.Time2.AddYears(1).Year)).ToList()); + } + [Fact] + public void DateTime_Parse() + { + var data = new List(); + data.Add(select.Where(a => DateTime.Parse(a.CreateTime.ToString()) > DateTime.Now).ToList()); + data.Add(select.Where(a => DateTime.Parse(a.Type.Time.AddYears(1).ToString()) > DateTime.Now).ToList()); + data.Add(select.Where(a => DateTime.Parse(a.Type.Parent.Time2.AddYears(1).ToString()) > DateTime.Now).ToList()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/MathTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/MathTest.cs new file mode 100644 index 00000000..3024b5ac --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/MathTest.cs @@ -0,0 +1,156 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTongExpression +{ + public class MathTest + { + + ISelect select => g.shentong.Select(); + + [Table(Name = "tb_topic")] + class Topic + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public int TypeGuid { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + class TestTypeInfo + { + public int Guid { get; set; } + public int ParentId { get; set; } + public TestTypeParentInfo Parent { get; set; } + public string Name { get; set; } + } + class TestTypeParentInfo + { + public int Id { get; set; } + public string Name { get; set; } + + public List Types { get; set; } + } + + [Fact] + public void PI() + { + var data = new List(); + data.Add(select.Where(a => Math.PI + a.Clicks > 0).ToList()); + } + [Fact] + public void Abs() + { + var data = new List(); + data.Add(select.Where(a => Math.Abs(-a.Clicks) > 0).ToList()); + } + [Fact] + public void Sign() + { + var data = new List(); + data.Add(select.Where(a => Math.Sign(-a.Clicks) > 0).ToList()); + } + [Fact] + public void Floor() + { + var data = new List(); + data.Add(select.Where(a => Math.Floor(a.Clicks + 0.5) == a.Clicks).ToList()); + } + [Fact] + public void Ceiling() + { + var data = new List(); + data.Add(select.Where(a => Math.Ceiling(a.Clicks + 0.5) == a.Clicks + 1).ToList()); + } + [Fact] + public void Round() + { + var data = new List(); + data.Add(select.Where(a => Math.Round(a.Clicks + 0.5) == a.Clicks).ToList()); + data.Add(select.Where(a => Math.Round(a.Clicks + 0.5, 1) > a.Clicks).ToList()); + } + [Fact] + public void Exp() + { + var data = new List(); + data.Add(select.Where(a => Math.Exp(1) == a.Clicks + 1).ToList()); + } + [Fact] + public void Log() + { + var data = new List(); + //data.Add(select.Where(a => Math.Log(a.Clicks + 0.5) == a.Clicks + 1).ToList()); + } + [Fact] + public void Log10() + { + var data = new List(); + //data.Add(select.Where(a => Math.Log10(a.Clicks + 0.5) == a.Clicks + 1).ToList()); + } + [Fact] + public void Pow() + { + var data = new List(); + data.Add(select.Where(a => Math.Pow(2, a.Clicks) == a.Clicks + 1).ToList()); + } + [Fact] + public void Sqrt() + { + var data = new List(); + data.Add(select.Where(a => Math.Sqrt(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Cos() + { + var data = new List(); + data.Add(select.Where(a => Math.Cos(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Sin() + { + var data = new List(); + data.Add(select.Where(a => Math.Sin(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Tan() + { + var data = new List(); + data.Add(select.Where(a => Math.Tan(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Acos() + { + var data = new List(); + //data.Add(select.Where(a => Math.Acos(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Asin() + { + var data = new List(); + //data.Add(select.Where(a => Math.Asin(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Atan() + { + var data = new List(); + data.Add(select.Where(a => Math.Atan(Math.Pow(2, a.Clicks)) == a.Clicks + 1).ToList()); + } + [Fact] + public void Atan2() + { + var data = new List(); + data.Add(select.Where(a => Math.Atan2(2, a.Clicks) == a.Clicks + 1).ToList()); + } + [Fact] + public void Truncate() + { + var data = new List(); + data.Add(select.Where(a => Math.Truncate(a.Clicks * 1.0 / 3) == a.Clicks + 1).ToList()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/OtherTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/OtherTest.cs new file mode 100644 index 00000000..8c9b039a --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/OtherTest.cs @@ -0,0 +1,221 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using Xunit; + +namespace FreeSql.Tests.ShenTongExpression +{ + public class OtherTest + { + + ISelect select => g.shentong.Select(); + + [Fact] + public void Div() + { + var t1 = select.Where(a => a.testFieldInt / 3 > 3).Limit(10).ToList(); + var t2 = select.Where(a => a.testFieldLong / 3 > 3).Limit(10).ToList(); + var t3 = select.Where(a => a.testFieldShort / 3 > 3).Limit(10).ToList(); + + var t4 = select.Where(a => a.testFieldInt / 3.0 > 3).Limit(10).ToList(); + var t5 = select.Where(a => a.testFieldLong / 3.0 > 3).Limit(10).ToList(); + var t6 = select.Where(a => a.testFieldShort / 3.0 > 3).Limit(10).ToList(); + + var t7 = select.Where(a => a.testFieldDouble / 3 > 3).Limit(10).ToList(); + var t8 = select.Where(a => a.testFieldDecimal / 3 > 3).Limit(10).ToList(); + var t9 = select.Where(a => a.testFieldFloat / 3 > 3).Limit(10).ToList(); + } + + [Fact] + public void Boolean() + { + var t1 = select.Where(a => a.testFieldBool == true).Limit(10).ToList(); + var t2 = select.Where(a => a.testFieldBool != true).Limit(10).ToList(); + var t3 = select.Where(a => a.testFieldBool == false).Limit(10).ToList(); + var t4 = select.Where(a => !a.testFieldBool).Limit(10).ToList(); + var t5 = select.Where(a => a.testFieldBool).Limit(10).ToList(); + var t51 = select.WhereCascade(a => a.testFieldBool).Limit(10).ToList(); + + var t11 = select.Where(a => a.testFieldBoolNullable == true).Limit(10).ToList(); + var t22 = select.Where(a => a.testFieldBoolNullable != true).Limit(10).ToList(); + var t33 = select.Where(a => a.testFieldBoolNullable == false).Limit(10).ToList(); + var t44 = select.Where(a => !a.testFieldBoolNullable.Value).Limit(10).ToList(); + var t55 = select.Where(a => a.testFieldBoolNullable.Value).Limit(10).ToList(); + + var t111 = select.Where(a => a.testFieldBool == true && a.Id > 0).Limit(10).ToList(); + var t222 = select.Where(a => a.testFieldBool != true && a.Id > 0).Limit(10).ToList(); + var t333 = select.Where(a => a.testFieldBool == false && a.Id > 0).Limit(10).ToList(); + var t444 = select.Where(a => !a.testFieldBool && a.Id > 0).Limit(10).ToList(); + var t555 = select.Where(a => a.testFieldBool && a.Id > 0).Limit(10).ToList(); + + var t1111 = select.Where(a => a.testFieldBoolNullable == true && a.Id > 0).Limit(10).ToList(); + var t2222 = select.Where(a => a.testFieldBoolNullable != true && a.Id > 0).Limit(10).ToList(); + var t3333 = select.Where(a => a.testFieldBoolNullable == false && a.Id > 0).Limit(10).ToList(); + var t4444 = select.Where(a => !a.testFieldBoolNullable.Value && a.Id > 0).Limit(10).ToList(); + var t5555 = select.Where(a => a.testFieldBoolNullable.Value && a.Id > 0).Limit(10).ToList(); + + var t11111 = select.Where(a => a.testFieldBool == true && a.Id > 0 && a.testFieldBool == true).Limit(10).ToList(); + var t22222 = select.Where(a => a.testFieldBool != true && a.Id > 0 && a.testFieldBool != true).Limit(10).ToList(); + var t33333 = select.Where(a => a.testFieldBool == false && a.Id > 0 && a.testFieldBool == false).Limit(10).ToList(); + var t44444 = select.Where(a => !a.testFieldBool && a.Id > 0 && !a.testFieldBool).Limit(10).ToList(); + var t55555 = select.Where(a => a.testFieldBool && a.Id > 0 && a.testFieldBool).Limit(10).ToList(); + + var t111111 = select.Where(a => a.testFieldBoolNullable == true && a.Id > 0 && a.testFieldBoolNullable == true).Limit(10).ToList(); + var t222222 = select.Where(a => a.testFieldBoolNullable != true && a.Id > 0 && a.testFieldBoolNullable != true).Limit(10).ToList(); + var t333333 = select.Where(a => a.testFieldBoolNullable == false && a.Id > 0 && a.testFieldBoolNullable == false).Limit(10).ToList(); + var t444444 = select.Where(a => !a.testFieldBoolNullable.Value && a.Id > 0 && !a.testFieldBoolNullable.Value).Limit(10).ToList(); + var t555555 = select.Where(a => a.testFieldBoolNullable.Value && a.Id > 0 && a.testFieldBoolNullable.Value).Limit(10).ToList(); + } + + [Fact] + public void Array() + { + //g.shentong.Aop.CurdAfter = (s, e) => { + // Trace.WriteLine(e.CurdType + ": " + e.ElapsedMilliseconds + "ms " + e.Sql.Replace("\n", "")); + //}; + IEnumerable testlinqlist = new List(new[] { 1, 2, 3 }); + var testlinq = select.Where(a => testlinqlist.Contains(a.testFieldInt)).ToList(); + + //var sql1 = select.Where(a => a.testFieldIntArray.Contains(1)).ToList(); + //var sql2 = select.Where(a => a.testFieldIntArray.Contains(1) == false).ToList(); + //var sql121 = select.Where(a => a.testFieldStringArray.Contains("aaa") == false).ToList(); + + //in not in + var sql111 = select.Where(a => new[] { 1, 2, 3 }.Contains(a.testFieldInt)).ToList(); + var sql112 = select.Where(a => new[] { 1, 2, 3 }.Contains(a.testFieldInt) == false).ToList(); + var sql113 = select.Where(a => !new[] { 1, 2, 3 }.Contains(a.testFieldInt)).ToList(); + + var inarray = new[] { 1, 2, 3 }; + var sql1111 = select.Where(a => inarray.Contains(a.testFieldInt)).ToSql(); + var sql1122 = select.Where(a => inarray.Contains(a.testFieldInt) == false).ToSql(); + var sql1133 = select.Where(a => !inarray.Contains(a.testFieldInt)).ToSql(); + + //in not in + var sql11111 = select.Where(a => new List() { 1, 2, 3 }.Contains(a.testFieldInt)).ToList(); + var sql11222 = select.Where(a => new List() { 1, 2, 3 }.Contains(a.testFieldInt) == false).ToList(); + var sql11333 = select.Where(a => !new List() { 1, 2, 3 }.Contains(a.testFieldInt)).ToList(); + + var sql11111a = select.Where(a => new List(new[] { 1, 2, 3 }).Contains(a.testFieldInt)).ToList(); + var sql11222b = select.Where(a => new List(new[] { 1, 2, 3 }).Contains(a.testFieldInt) == false).ToList(); + var sql11333c = select.Where(a => !new List(new[] { 1, 2, 3 }).Contains(a.testFieldInt)).ToList(); + + var inarray2 = new List() { 1, 2, 3 }; + var sql111111 = select.Where(a => inarray.Contains(a.testFieldInt)).ToList(); + var sql112222 = select.Where(a => inarray.Contains(a.testFieldInt) == false).ToList(); + var sql113333 = select.Where(a => !inarray.Contains(a.testFieldInt)).ToList(); + + //var sql1111112 = select.ToList(a => inarray); + //var sql1111113 = select.ToList(a => a.testFieldIntArray); + + + //var sql3 = select.Where(a => a.testFieldIntArray.Any()).ToList(); + //var sql4 = select.Where(a => a.testFieldIntArray.Any() == false).ToList(); + + //var sql5 = select.ToList(a => a.testFieldIntArray.Concat(new[] { 1, 2, 3 })); + + //var sql6 = select.Where(a => a.testFieldIntArray.GetLength(1) > 0).ToList(); + //var sql7 = select.Where(a => a.testFieldIntArray.GetLongLength(1) > 0).ToList(); + //var sql8 = select.Where(a => a.testFieldIntArray.Length > 0).ToList(); + //var sql9 = select.Where(a => a.testFieldIntArray.Count() > 0).ToList(); + + var inarray2n = Enumerable.Range(1, 3333).ToArray(); + var sql1111111 = select.Where(a => inarray2n.Contains(a.testFieldInt)).ToList(); + var sql1122222 = select.Where(a => inarray2n.Contains(a.testFieldInt) == false).ToList(); + var sql1133333 = select.Where(a => !inarray2n.Contains(a.testFieldInt)).ToList(); + } + + [Table(Name = "tb_alltype")] + class TableAllType + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + + public bool testFieldBool { get; set; } + public sbyte testFieldSByte { get; set; } + public short testFieldShort { get; set; } + public int testFieldInt { get; set; } + public long testFieldLong { get; set; } + public byte testFieldByte { get; set; } + public ushort testFieldUShort { get; set; } + public uint testFieldUInt { get; set; } + public ulong testFieldULong { get; set; } + public double testFieldDouble { get; set; } + public float testFieldFloat { get; set; } + public decimal testFieldDecimal { get; set; } + public TimeSpan testFieldTimeSpan { get; set; } + public DateTime testFieldDateTime { get; set; } + public byte[] testFieldBytes { get; set; } + public string testFieldString { get; set; } + public Guid testFieldGuid { get; set; } + + public bool? testFieldBoolNullable { get; set; } + public sbyte? testFieldSByteNullable { get; set; } + public short? testFieldShortNullable { get; set; } + public int? testFieldIntNullable { get; set; } + public long? testFielLongNullable { get; set; } + public byte? testFieldByteNullable { get; set; } + public ushort? testFieldUShortNullable { get; set; } + public uint? testFieldUIntNullable { get; set; } + public ulong? testFieldULongNullable { get; set; } + public double? testFieldDoubleNullable { get; set; } + public float? testFieldFloatNullable { get; set; } + public decimal? testFieldDecimalNullable { get; set; } + public TimeSpan? testFieldTimeSpanNullable { get; set; } + public DateTime? testFieldDateTimeNullable { get; set; } + public Guid? testFieldGuidNullable { get; set; } + + public TableAllTypeEnumType1 testFieldEnum1 { get; set; } + public TableAllTypeEnumType1? testFieldEnum1Nullable { get; set; } + public TableAllTypeEnumType2 testFieldEnum2 { get; set; } + public TableAllTypeEnumType2? testFieldEnum2Nullable { get; set; } + + ///* array */ + //public bool[] testFieldBoolArray { get; set; } + //public sbyte[] testFieldSByteArray { get; set; } + //public short[] testFieldShortArray { get; set; } + //public int[] testFieldIntArray { get; set; } + //public long[] testFieldLongArray { get; set; } + //public byte[] testFieldByteArray { get; set; } + //public ushort[] testFieldUShortArray { get; set; } + //public uint[] testFieldUIntArray { get; set; } + //public ulong[] testFieldULongArray { get; set; } + //public double[] testFieldDoubleArray { get; set; } + //public float[] testFieldFloatArray { get; set; } + //public decimal[] testFieldDecimalArray { get; set; } + //public TimeSpan[] testFieldTimeSpanArray { get; set; } + //public DateTime[] testFieldDateTimeArray { get; set; } + //public byte[][] testFieldBytesArray { get; set; } + //public string[] testFieldStringArray { get; set; } + //public Guid[] testFieldGuidArray { get; set; } + + //public bool?[] testFieldBoolArrayNullable { get; set; } + //public sbyte?[] testFieldSByteArrayNullable { get; set; } + //public short?[] testFieldShortArrayNullable { get; set; } + //public int?[] testFieldIntArrayNullable { get; set; } + //public long?[] testFielLongArrayNullable { get; set; } + //public byte?[] testFieldByteArrayNullable { get; set; } + //public ushort?[] testFieldUShortArrayNullable { get; set; } + //public uint?[] testFieldUIntArrayNullable { get; set; } + //public ulong?[] testFieldULongArrayNullable { get; set; } + //public double?[] testFieldDoubleArrayNullable { get; set; } + //public float?[] testFieldFloatArrayNullable { get; set; } + //public decimal?[] testFieldDecimalArrayNullable { get; set; } + //public TimeSpan?[] testFieldTimeSpanArrayNullable { get; set; } + //public DateTime?[] testFieldDateTimeArrayNullable { get; set; } + //public Guid?[] testFieldGuidArrayNullable { get; set; } + + //public TableAllTypeEnumType1[] testFieldEnum1Array { get; set; } + //public TableAllTypeEnumType1?[] testFieldEnum1ArrayNullable { get; set; } + //public TableAllTypeEnumType2[] testFieldEnum2Array { get; set; } + //public TableAllTypeEnumType2?[] testFieldEnum2ArrayNullable { get; set; } + } + + public enum TableAllTypeEnumType1 { e1, e2, e3, e5 } + [Flags] public enum TableAllTypeEnumType2 { f1, f2, f3 } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/StringTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/StringTest.cs new file mode 100644 index 00000000..c3818765 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/StringTest.cs @@ -0,0 +1,728 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTongExpression +{ + public class StringTest + { + + ISelect select => g.shentong.Select(); + + [Table(Name = "tb_topic")] + class Topic + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public int TypeGuid { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + class TestTypeInfo + { + [Column(IsIdentity = true)] + public int Guid { get; set; } + public int ParentId { get; set; } + public TestTypeParentInfo Parent { get; set; } + public string Name { get; set; } + } + class TestTypeParentInfo + { + public int Id { get; set; } + public string Name { get; set; } + + public List Types { get; set; } + } + class TestEqualsGuid + { + public Guid id { get; set; } + } + + [Fact] + public void Equals__() + { + var list = new List(); + list.Add(select.Where(a => a.Title.Equals("aaa")).ToList()); + list.Add(g.shentong.Select().Where(a => a.id.Equals(Guid.Empty)).ToList()); + } + + + [Fact] + public void Empty() + { + var data = new List(); + data.Add(select.Where(a => (a.Title ?? "") == string.Empty).ToSql()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (ifnull(a.`Title`, '') = '') + } + + [Fact] + public void StartsWith() + { + var list = new List(); + list.Add(select.Where(a => a.Title.StartsWith("aaa")).ToList()); + list.Add(select.Where(a => a.Title.StartsWith(a.Title)).ToList()); + list.Add(select.Where(a => a.Title.StartsWith(a.Title + 1)).ToList()); + list.Add(select.Where(a => a.Title.StartsWith(a.Type.Name)).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE '%aaa') + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE concat('%', a.`Title`)) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE concat('%', concat(a.`Title`, 1))) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE((a.`Title`) LIKE concat('%', a__Type.`Name`)) + list.Add(select.Where(a => (a.Title + "aaa").StartsWith("aaa")).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").StartsWith(a.Title)).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").StartsWith(a.Title + 1)).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").StartsWith(a.Type.Name)).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE '%aaa') + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat('%', a.`Title`)) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat('%', concat(a.`Title`, 1))) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat('%', a__Type.`Name`)) + } + [Fact] + public void EndsWith() + { + var list = new List(); + list.Add(select.Where(a => a.Title.EndsWith("aaa")).ToList()); + list.Add(select.Where(a => a.Title.EndsWith(a.Title)).ToList()); + list.Add(select.Where(a => a.Title.EndsWith(a.Title + 1)).ToList()); + list.Add(select.Where(a => a.Title.EndsWith(a.Type.Name)).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE 'aaa%') + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE concat(a.`Title`, '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE concat(concat(a.`Title`, 1), '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE((a.`Title`) LIKE concat(a__Type.`Name`, '%')) + list.Add(select.Where(a => (a.Title + "aaa").EndsWith("aaa")).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").EndsWith(a.Title)).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").EndsWith(a.Title + 1)).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").EndsWith(a.Type.Name)).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE 'aaa%') + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat(a.`Title`, '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat(concat(a.`Title`, 1), '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat(a__Type.`Name`, '%')) + } + [Fact] + public void Contains() + { + var list = new List(); + list.Add(select.Where(a => a.Title.Contains("aaa")).ToList()); + list.Add(select.Where(a => a.Title.Contains(a.Title)).ToList()); + list.Add(select.Where(a => a.Title.Contains(a.Title + 1)).ToList()); + list.Add(select.Where(a => a.Title.Contains(a.Type.Name)).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE '%aaa%') + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE concat('%', a.`Title`, '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((a.`Title`) LIKE concat('%', a.`Title` +1, '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE((a.`Title`) LIKE concat('%', a__Type.`Name`, '%')) + list.Add(select.Where(a => (a.Title + "aaa").Contains("aaa")).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").Contains(a.Title)).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").Contains(a.Title + 1)).ToList()); + list.Add(select.Where(a => (a.Title + "aaa").Contains(a.Type.Name)).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE '%aaa%') + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat('%', a.`Title`, '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat('%', concat(a.`Title`, 1), '%')) + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE((concat(a.`Title`, 'aaa')) LIKE concat('%', a__Type.`Name`, '%')) + } + [Fact] + public void ToLower() + { + var data = new List(); + data.Add(select.Where(a => a.Title.ToLower() == "aaa").ToList()); + data.Add(select.Where(a => a.Title.ToLower() == a.Title).ToList()); + data.Add(select.Where(a => a.Title.ToLower() == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.ToLower() == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE(lower(a.`Title`) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE(lower(a.`Title`) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE(lower(a.`Title`) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE(lower(a.`Title`) = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.ToLower() + "aaa").ToLower() == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.ToLower() + "aaa").ToLower() == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.ToLower() + "aaa").ToLower() == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.ToLower() + "aaa").ToLower() == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE(lower(concat(lower(a.`Title`), 'aaa')) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE(lower(concat(lower(a.`Title`), 'aaa')) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE(lower(concat(lower(a.`Title`), 'aaa')) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE(lower(concat(lower(a.`Title`), 'aaa')) = a__Type.`Name`) + } + [Fact] + public void ToUpper() + { + var data = new List(); + data.Add(select.Where(a => a.Title.ToUpper() == "aaa").ToList()); + data.Add(select.Where(a => a.Title.ToUpper() == a.Title).ToList()); + data.Add(select.Where(a => a.Title.ToUpper() == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.ToUpper() == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (upper(a.`Title`) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (upper(a.`Title`) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (upper(a.`Title`) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (upper(a.`Title`) = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.ToUpper() + "aaa").ToUpper() == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.ToUpper() + "aaa").ToUpper() == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.ToUpper() + "aaa").ToUpper() == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.ToUpper() + "aaa").ToUpper() == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (upper(concat(upper(a.`Title`), 'aaa')) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (upper(concat(upper(a.`Title`), 'aaa')) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (upper(concat(upper(a.`Title`), 'aaa')) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (upper(concat(upper(a.`Title`), 'aaa')) = a__Type.`Name`) + } + [Fact] + public void Substring() + { + var data = new List(); + data.Add(select.Where(a => a.Title.Substring(0) == "aaa").ToList()); + data.Add(select.Where(a => a.Title.Substring(0) == a.Title).ToList()); + data.Add(select.Where(a => a.Title.Substring(0) == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.Substring(0) == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (substr(a.`Title`, 1) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (substr(a.`Title`, 1) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (substr(a.`Title`, 1) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (substr(a.`Title`, 1) = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.Substring(0) + "aaa").Substring(a.Title.Length) == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.Substring(0) + "aaa").Substring(0, a.Title.Length) == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.Substring(0) + "aaa").Substring(0, 3) == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.Substring(0) + "aaa").Substring(1, 2) == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (substr(concat(substr(a.`Title`, 1), 'aaa'), char_length(a.`Title`) + 1) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (substr(concat(substr(a.`Title`, 1), 'aaa'), 1, char_length(a.`Title`)) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (substr(concat(substr(a.`Title`, 1), 'aaa'), 1, 3) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (substr(concat(substr(a.`Title`, 1), 'aaa'), 2, 2) = a__Type.`Name`) + } + [Fact] + public void Length() + { + var data = new List(); + data.Add(select.Where(a => a.Title.Length == 0).ToList()); + data.Add(select.Where(a => a.Title.Length == 1).ToList()); + data.Add(select.Where(a => a.Title.Length == a.Title.Length + 1).ToList()); + data.Add(select.Where(a => a.Title.Length == a.Type.Name.Length).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (char_length(a.`Title`) = 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (char_length(a.`Title`) = 1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (char_length(a.`Title`) = char_length(a.`Title`) + 1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (char_length(a.`Title`) = char_length(a__Type.`Name`)); + data.Add(select.Where(a => (a.Title + "aaa").Length == 0).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").Length == 1).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").Length == a.Title.Length + 1).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").Length == a.Type.Name.Length).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (char_length(concat(a.`Title`, 'aaa')) = 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (char_length(concat(a.`Title`, 'aaa')) = 1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (char_length(concat(a.`Title`, 'aaa')) = char_length(a.`Title`) + 1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (char_length(concat(a.`Title`, 'aaa')) = char_length(a__Type.`Name`)) + } + [Fact] + public void IndexOf() + { + var data = new List(); + data.Add(select.Where(a => a.Title.IndexOf("aaa") == -1).ToList()); + data.Add(select.Where(a => a.Title.IndexOf("aaa", 2) == -1).ToList()); + data.Add(select.Where(a => a.Title.IndexOf("aaa", 2) == (a.Title.Length + 1)).ToList()); + data.Add(select.Where(a => a.Title.IndexOf("aaa", 2) == a.Type.Name.Length + 1).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((locate(a.`Title`, 'aaa') - 1) = -1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((locate(a.`Title`, 'aaa', 3) - 1) = -1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((locate(a.`Title`, 'aaa', 3) - 1) = char_length(a.`Title`) + 1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE ((locate(a.`Title`, 'aaa', 3) - 1) = char_length(a__Type.`Name`) + 1); + data.Add(select.Where(a => (a.Title + "aaa").IndexOf("aaa") == -1).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").IndexOf("aaa", 2) == -1).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").IndexOf("aaa", 2) == (a.Title.Length + 1)).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").IndexOf("aaa", 2) == a.Type.Name.Length + 1).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((locate(concat(a.`Title`, 'aaa'), 'aaa') - 1) = -1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((locate(concat(a.`Title`, 'aaa'), 'aaa', 3) - 1) = -1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((locate(concat(a.`Title`, 'aaa'), 'aaa', 3) - 1) = char_length(a.`Title`) + 1); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE ((locate(concat(a.`Title`, 'aaa'), 'aaa', 3) - 1) = char_length(a__Type.`Name`) + 1) + } + [Fact] + public void PadLeft() + { + var data = new List(); + data.Add(select.Where(a => a.Title.PadLeft(10, 'a') == "aaa").ToList()); + data.Add(select.Where(a => a.Title.PadLeft(10, 'a') == a.Title).ToList()); + data.Add(select.Where(a => a.Title.PadLeft(10, 'a') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.PadLeft(10, 'a') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (lpad(a.`Title`, 10, 'a') = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (lpad(a.`Title`, 10, 'a') = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (lpad(a.`Title`, 10, 'a') = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (lpad(a.`Title`, 10, 'a') = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.PadLeft(10, 'a') + "aaa").PadLeft(20, 'b') == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.PadLeft(10, 'a') + "aaa").PadLeft(20, 'b') == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.PadLeft(10, 'a') + "aaa").PadLeft(20, 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.PadLeft(10, 'a') + "aaa").PadLeft(20, 'b') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (lpad(concat(lpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (lpad(concat(lpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (lpad(concat(lpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (lpad(concat(lpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = a__Type.`Name`) + } + [Fact] + public void PadRight() + { + var data = new List(); + data.Add(select.Where(a => a.Title.PadRight(10, 'a') == "aaa").ToList()); + data.Add(select.Where(a => a.Title.PadRight(10, 'a') == a.Title).ToList()); + data.Add(select.Where(a => a.Title.PadRight(10, 'a') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.PadRight(10, 'a') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rpad(a.`Title`, 10, 'a') = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rpad(a.`Title`, 10, 'a') = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rpad(a.`Title`, 10, 'a') = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (rpad(a.`Title`, 10, 'a') = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.PadRight(10, 'a') + "aaa").PadRight(20, 'b') == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.PadRight(10, 'a') + "aaa").PadRight(20, 'b') == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.PadRight(10, 'a') + "aaa").PadRight(20, 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.PadRight(10, 'a') + "aaa").PadRight(20, 'b') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rpad(concat(rpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rpad(concat(rpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rpad(concat(rpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (rpad(concat(rpad(a.`Title`, 10, 'a'), 'aaa'), 20, 'b') = a__Type.`Name`) + } + [Fact] + public void Trim() + { + var data = new List(); + data.Add(select.Where(a => a.Title.Trim() == "aaa").ToList()); + data.Add(select.Where(a => a.Title.Trim('a') == a.Title).ToList()); + data.Add(select.Where(a => a.Title.Trim('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.Trim('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(a.`Title`) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim('a' from a.`Title`) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim('b' from trim('a' from a.`Title`)) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (trim('c' from trim('b' from trim('a' from a.`Title`))) = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.Trim() + "aaa").Trim() == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.Trim('a') + "aaa").Trim('a') == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.Trim('a', 'b') + "aaa").Trim('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.Trim('a', 'b', 'c') + "aaa").Trim('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(concat(trim(a.`Title`), 'aaa')) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim('a' from concat(trim('a' from a.`Title`), 'aaa')) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim('b' from trim('a' from concat(trim('b' from trim('a' from a.`Title`)), 'aaa'))) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (trim('c' from trim('b' from trim('a' from concat(trim('c' from trim('b' from trim('a' from a.`Title`))), 'aaa')))) = a__Type.`Name`) + } + [Fact] + public void TrimStart() + { + var data = new List(); + data.Add(select.Where(a => a.Title.TrimStart() == "aaa").ToList()); + data.Add(select.Where(a => a.Title.TrimStart('a') == a.Title).ToList()); + data.Add(select.Where(a => a.Title.TrimStart('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.TrimStart('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (ltrim(a.`Title`) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'a' from trim(leading 'a' from a.`Title`)) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'b' from trim(leading 'b' from trim(trailing 'a' from trim(leading 'a' from a.`Title`)))) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (trim(trailing 'c' from trim(leading 'c' from trim(trailing 'b' from trim(leading 'b' from trim(trailing 'a' from trim(leading 'a' from a.`Title`)))))) = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.TrimStart() + "aaa").TrimStart() == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.TrimStart('a') + "aaa").TrimStart('a') == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.TrimStart('a', 'b') + "aaa").TrimStart('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.TrimStart('a', 'b', 'c') + "aaa").TrimStart('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (ltrim(concat(ltrim(a.`Title`), 'aaa')) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'a' from trim(leading 'a' from concat(trim(trailing 'a' from trim(leading 'a' from a.`Title`)), 'aaa'))) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'b' from trim(leading 'b' from trim(trailing 'a' from trim(leading 'a' from concat(trim(trailing 'b' from trim(leading 'b' from trim(trailing 'a' from trim(leading 'a' from a.`Title`)))), 'aaa'))))) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (trim(trailing 'c' from trim(leading 'c' from trim(trailing 'b' from trim(leading 'b' from trim(trailing 'a' from trim(leading 'a' from concat(trim(trailing 'c' from trim(leading 'c' from trim(trailing 'b' from trim(leading 'b' from trim(trailing 'a' from trim(leading 'a' from a.`Title`)))))), 'aaa'))))))) = a__Type.`Name`) + } + [Fact] + public void TrimEnd() + { + var data = new List(); + data.Add(select.Where(a => a.Title.TrimEnd() == "aaa").ToList()); + data.Add(select.Where(a => a.Title.TrimEnd('a') == a.Title).ToList()); + data.Add(select.Where(a => a.Title.TrimEnd('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.TrimEnd('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rtrim(a.`Title`) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'a' from a.`Title`) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'b' from trim(trailing 'a' from a.`Title`)) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (trim(trailing 'c' from trim(trailing 'b' from trim(trailing 'a' from a.`Title`))) = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.TrimEnd() + "aaa").TrimEnd() == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.TrimEnd('a') + "aaa").TrimEnd('a') == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.TrimEnd('a', 'b') + "aaa").TrimEnd('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.TrimEnd('a', 'b', 'c') + "aaa").TrimEnd('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (rtrim(concat(rtrim(a.`Title`), 'aaa')) = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'a' from concat(trim(trailing 'a' from a.`Title`), 'aaa')) = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (trim(trailing 'b' from trim(trailing 'a' from concat(trim(trailing 'b' from trim(trailing 'a' from a.`Title`)), 'aaa'))) = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (trim(trailing 'c' from trim(trailing 'b' from trim(trailing 'a' from concat(trim(trailing 'c' from trim(trailing 'b' from trim(trailing 'a' from a.`Title`))), 'aaa')))) = a__Type.`Name`) + } + [Fact] + public void Replace() + { + var data = new List(); + data.Add(select.Where(a => a.Title.Replace("a", "b") == "aaa").ToList()); + data.Add(select.Where(a => a.Title.Replace("a", "b").Replace("b", "c") == a.Title).ToList()); + data.Add(select.Where(a => a.Title.Replace("a", "b").Replace("b", "c").Replace("c", "a") == (a.Title + 1)).ToList()); + data.Add(select.Where(a => a.Title.Replace("a", "b").Replace("b", "c").Replace(a.Type.Name, "a") == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (replace(a.`Title`, 'a', 'b') = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (replace(replace(a.`Title`, 'a', 'b'), 'b', 'c') = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (replace(replace(replace(a.`Title`, 'a', 'b'), 'b', 'c'), 'c', 'a') = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (replace(replace(replace(a.`Title`, 'a', 'b'), 'b', 'c'), a__Type.`Name`, 'a') = a__Type.`Name`); + data.Add(select.Where(a => (a.Title.Replace("a", "b") + "aaa").TrimEnd() == "aaa").ToList()); + data.Add(select.Where(a => (a.Title.Replace("a", "b").Replace("b", "c") + "aaa").TrimEnd('a') == a.Title).ToList()); + data.Add(select.Where(a => (a.Title.Replace("a", "b").Replace("b", "c").Replace("c", "a") + "aaa").TrimEnd('a', 'b') == (a.Title + 1)).ToList()); + data.Add(select.Where(a => (a.Title.Replace("a", "b").Replace("b", "c").Replace(a.Type.Name, "a") + "aaa").TrimEnd('a', 'b', 'c') == a.Type.Name).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (concat(replace(a.`Title`, 'a', 'b'), 'aaa') = 'aaa'); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (concat(replace(replace(a.`Title`, 'a', 'b'), 'b', 'c'), 'aaa') = a.`Title`); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (concat(replace(replace(replace(a.`Title`, 'a', 'b'), 'b', 'c'), 'c', 'a'), 'aaa') = concat(a.`Title`, 1)); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (concat(replace(replace(replace(a.`Title`, 'a', 'b'), 'b', 'c'), a__Type.`Name`, 'a'), 'aaa') = a__Type.`Name`) + } + [Fact] + public void CompareTo() + { + var data = new List(); + data.Add(select.Where(a => a.Title.CompareTo(a.Title) == 0).ToList()); + data.Add(select.Where(a => a.Title.CompareTo(a.Title) > 0).ToList()); + data.Add(select.Where(a => a.Title.CompareTo(a.Title + 1) == 0).ToList()); + data.Add(select.Where(a => a.Title.CompareTo(a.Title + a.Type.Name) == 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (strcmp(a.`Title`, a.`Title`) = 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (strcmp(a.`Title`, a.`Title`) > 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (strcmp(a.`Title`, concat(a.`Title`, 1)) = 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (strcmp(a.`Title`, concat(a.`Title`, a__Type.`Name`)) = 0); + data.Add(select.Where(a => (a.Title + "aaa").CompareTo("aaa") == 0).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").CompareTo(a.Title) > 0).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").CompareTo(a.Title + 1) == 0).ToList()); + data.Add(select.Where(a => (a.Title + "aaa").CompareTo(a.Type.Name) == 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (strcmp(concat(a.`Title`, 'aaa'), 'aaa') = 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (strcmp(concat(a.`Title`, 'aaa'), a.`Title`) > 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (strcmp(concat(a.`Title`, 'aaa'), concat(a.`Title`, 1)) = 0); + + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a__Type.`Guid` as4, a__Type.`ParentId` as5, a__Type.`Name` as6, a.`Title` as7, a.`CreateTime` as8 + //FROM `tb_topic` a, `TestTypeInfo` a__Type + //WHERE (strcmp(concat(a.`Title`, 'aaa'), a__Type.`Name`) = 0) + } + + [Fact] + public void string_IsNullOrEmpty() + { + var data = new List(); + data.Add(select.Where(a => string.IsNullOrEmpty(a.Title)).ToList()); + data.Add(select.Where(a => string.IsNullOrEmpty(a.Title) == false).ToList()); + data.Add(select.Where(a => !string.IsNullOrEmpty(a.Title)).ToList()); + } + + [Fact] + public void string_IsNullOrWhiteSpace() + { + var data = new List(); + data.Add(select.Where(a => string.IsNullOrWhiteSpace(a.Title)).ToList()); + data.Add(select.Where(a => string.IsNullOrWhiteSpace(a.Title) == false).ToList()); + data.Add(select.Where(a => !string.IsNullOrWhiteSpace(a.Title)).ToList()); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/TimeSpanTest.cs b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/TimeSpanTest.cs new file mode 100644 index 00000000..00b5e651 --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/ShenTong/ShenTongExpression/TimeSpanTest.cs @@ -0,0 +1,293 @@ +using FreeSql.DataAnnotations; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.ShenTongExpression +{ + public class TimeSpanTest + { + + ISelect select => g.shentong.Select(); + + [Table(Name = "tb_topic")] + class Topic + { + [Column(IsIdentity = true, IsPrimary = true)] + public int Id { get; set; } + public int Clicks { get; set; } + public int TypeGuid { get; set; } + public TestTypeInfo Type { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + } + class TestTypeInfo + { + public int Guid { get; set; } + public int ParentId { get; set; } + public TestTypeParentInfo Parent { get; set; } + public string Name { get; set; } + } + class TestTypeParentInfo + { + public int Id { get; set; } + public string Name { get; set; } + + public List Types { get; set; } + } + [Fact] + public void Zero() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay > TimeSpan.Zero).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) > 0) + } + [Fact] + public void MinValue() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay > TimeSpan.MinValue).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) > -922337203685477580) + } + [Fact] + public void MaxValue() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay < TimeSpan.MaxValue).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) < 922337203685477580) + } + [Fact] + public void Days() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Days == 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) div 86400000000) = 0) + } + [Fact] + public void Hours() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Hours > 0).ToSql()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) div 3600000000) mod 24 > 0) + } + [Fact] + public void Milliseconds() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Milliseconds > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) div 1000 mod 1000) > 0) + } + [Fact] + public void Minutes() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Minutes > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) div 60000000 mod 60) > 0) + } + [Fact] + public void Seconds() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Seconds > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) div 1000000 mod 60) > 0) + } + [Fact] + public void Ticks() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Ticks > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) * 10) > 0) + } + [Fact] + public void TotalDays() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.TotalDays > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) / 86400000000) > 0) + } + [Fact] + public void TotalHours() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.TotalHours > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) / 3600000000) > 0) + } + [Fact] + public void TotalMilliseconds() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.TotalMilliseconds > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) / 1000) > 0) + } + [Fact] + public void TotalMinutes() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.TotalMinutes > 0).ToSql()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) / 60000000) > 0) + } + [Fact] + public void TotalSeconds() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.TotalSeconds > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) / 1000000) > 0) + } + [Fact] + public void Add() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Add(TimeSpan.FromDays(1)) > TimeSpan.Zero).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) + (1 * 86400000000)) > 0) + } + [Fact] + public void Subtract() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Subtract(TimeSpan.FromDays(1)) > TimeSpan.Zero).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) - (1 * 86400000000)) > 0) + } + [Fact] + public void CompareTo() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.CompareTo(TimeSpan.FromDays(1)) > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) - ((1 * 86400000000))) > 0) + } + [Fact] + public void this_Equals() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.Equals(TimeSpan.FromDays(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 86400000000))) + } + [Fact] + public void this_ToString() + { + var data = new List(); + data.Add(select.Where(a => a.CreateTime.TimeOfDay.ToString() == "ssss").ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (date_format(date_add(cast('0001/1/1 0:00:00' as datetime), interval (timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) microsecond), '%Y-%m-%d %H:%i:%s.%f') = 'ssss') + } + + [Fact] + public void TimeSpan_Compare() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Compare(a.CreateTime.TimeOfDay, TimeSpan.FromDays(1)) > 0).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) - ((1 * 86400000000))) > 0) + } + [Fact] + public void TimeSpan_Equals() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromDays(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 86400000000))) + } + [Fact] + public void TimeSpan_FromDays() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromDays(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 86400000000))) + } + [Fact] + public void TimeSpan_FromHours() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromHours(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 3600000000))) + } + [Fact] + public void TimeSpan_FromMilliseconds() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromMilliseconds(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 1000))) + } + [Fact] + public void TimeSpan_FromMinutes() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromMinutes(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 60000000))) + } + [Fact] + public void TimeSpan_FromSeconds() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromSeconds(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 * 1000000))) + } + [Fact] + public void TimeSpan_FromTicks() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Equals(a.CreateTime.TimeOfDay, TimeSpan.FromTicks(1))).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE ((timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000) = (1 / 10))) + } + [Fact] + public void TimeSpan_Parse() + { + var data = new List(); + data.Add(select.Where(a => TimeSpan.Parse(a.CreateTime.TimeOfDay.ToString()) > TimeSpan.Zero).ToList()); + //SELECT a.`Id` as1, a.`Clicks` as2, a.`TypeGuid` as3, a.`Title` as4, a.`CreateTime` as5 + //FROM `tb_topic` a + //WHERE (cast(date_format(date_add(cast('0001/1/1 0:00:00' as datetime), interval (timestampdiff(microsecond, date_format(a.`CreateTime`, '1970-1-1 %H:%i:%s.%f'), a.`CreateTime`) + 62135596800000000)) microsecond), '%Y-%m-%d %H:%i:%s.%f') as signed) > 0) + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/g.cs b/FreeSql.Tests/FreeSql.Tests/g.cs index 79f636fc..1fc3ae7d 100644 --- a/FreeSql.Tests/FreeSql.Tests/g.cs +++ b/FreeSql.Tests/FreeSql.Tests/g.cs @@ -120,4 +120,30 @@ public class g (cmd, traceLog) => Console.WriteLine(traceLog)) .Build()); public static IFreeSql dameng => damengLazy.Value; + + static Lazy shentongLazy = new Lazy(() => + { + var connString = new System.Data.OscarClient.OscarConnectionStringBuilder { + Host = "192.168.164.10", + Port = 2003, + UserName = "SYSDBA", + Password = "szoscar55", + Database = "OSRDB", + Pooling = true, + MaxPoolSize = 2 + }; + return new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.ShenTong, "HOST=192.168.164.10;PORT=2003;DATABASE=OSRDB;USERNAME=SYSDBA;PASSWORD=szoscar55;MAXPOOLSIZE=2") + //.UseConnectionFactory(FreeSql.DataType.ShenTong, () => new System.Data.OscarClient.OscarConnection("HOST=192.168.164.10;PORT=2003;DATABASE=OSRDB;USERNAME=SYSDBA;PASSWORD=szoscar55;MAXPOOLSIZE=2")) + .UseAutoSyncStructure(true) + //.UseGenerateCommandParameterWithLambda(true) + .UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper) + .UseLazyLoading(true) + .UseMonitorCommand( + cmd => Trace.WriteLine("\r\n线程" + Thread.CurrentThread.ManagedThreadId + ": " + cmd.CommandText) //监听SQL命令对象,在执行前 + //, (cmd, traceLog) => Console.WriteLine(traceLog) + ) + .Build(); + }); + public static IFreeSql shentong => shentongLazy.Value; } diff --git a/FreeSql.sln b/FreeSql.sln index 0f8259b1..c27f7820 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -78,7 +78,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.Linq", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Dameng", "Providers\FreeSql.Provider.Dameng\FreeSql.Provider.Dameng.csproj", "{E74D90E8-1CBC-4677-817B-1CA05AB97937}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aspnetcore_transaction", "Examples\aspnetcore_transaction\aspnetcore_transaction.csproj", "{07AB0B37-A8B1-4FB1-9259-7B804E369E36}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "aspnetcore_transaction", "Examples\aspnetcore_transaction\aspnetcore_transaction.csproj", "{07AB0B37-A8B1-4FB1-9259-7B804E369E36}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.ShenTong", "Providers\FreeSql.Provider.ShenTong\FreeSql.Provider.ShenTong.csproj", "{938173AF-157F-4040-AED3-171DA1809CAA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -486,6 +488,18 @@ Global {07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x64.Build.0 = Release|Any CPU {07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x86.ActiveCfg = Release|Any CPU {07AB0B37-A8B1-4FB1-9259-7B804E369E36}.Release|x86.Build.0 = Release|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Debug|x64.ActiveCfg = Debug|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Debug|x64.Build.0 = Debug|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Debug|x86.ActiveCfg = Debug|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Debug|x86.Build.0 = Debug|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Release|Any CPU.Build.0 = Release|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Release|x64.ActiveCfg = Release|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Release|x64.Build.0 = Release|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Release|x86.ActiveCfg = Release|Any CPU + {938173AF-157F-4040-AED3-171DA1809CAA}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -514,6 +528,7 @@ Global {57B3F5B0-D46A-4442-8EC6-9A9A784404B7} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} {E74D90E8-1CBC-4677-817B-1CA05AB97937} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {07AB0B37-A8B1-4FB1-9259-7B804E369E36} = {94C8A78D-AA15-47B2-A348-530CD86BFC1B} + {938173AF-157F-4040-AED3-171DA1809CAA} = {2A381C57-2697-427B-9F10-55DA11FD02E4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98} diff --git a/FreeSql/DataType.cs b/FreeSql/DataType.cs index 2a194b94..0d00c721 100644 --- a/FreeSql/DataType.cs +++ b/FreeSql/DataType.cs @@ -39,6 +39,11 @@ namespace FreeSql /// /// 北京人大金仓信息技术股份有限公司,基于 Odbc 的实现 /// - OdbcKingbaseES + OdbcKingbaseES, + + /// + /// 天津神舟通用数据技术有限公司,基于 System.Data.OscarClient.dll 的实现 + /// + ShenTong } } diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 97257dce..a029e482 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -556,6 +556,11 @@ 北京人大金仓信息技术股份有限公司,基于 Odbc 的实现 + + + 天津神舟通用数据技术有限公司,基于 System.Data.OscarClient.dll 的实现 + + 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 null @@ -1367,7 +1372,8 @@ Oracle: for update nowait Sqlite: 无效果 达梦: for update nowait - 人大金仓: for update nowait + 人大金仓: for update nowait + 神通: for update nowait noawait diff --git a/FreeSql/FreeSqlBuilder.cs b/FreeSql/FreeSqlBuilder.cs index 5602f681..f5e63e37 100644 --- a/FreeSql/FreeSqlBuilder.cs +++ b/FreeSql/FreeSqlBuilder.cs @@ -239,6 +239,11 @@ namespace FreeSql if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.KingbaseES.OdbcKingbaseESProvider<>"); break; + case DataType.ShenTong: + type = Type.GetType("FreeSql.ShenTong.ShenTongProvider`1,FreeSql.Provider.ShenTong")?.MakeGenericType(typeof(TMark)); + if (type == null) throwNotFind("FreeSql.Provider.ShenTong.dll", "FreeSql.ShenTong.ShenTongProvider<>"); + break; + default: throw new Exception("未指定 UseConnectionString 或者 UseConnectionFactory"); } } diff --git a/FreeSql/Interface/Curd/ISelect/ISelect0.cs b/FreeSql/Interface/Curd/ISelect/ISelect0.cs index ef2602cb..101503ed 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect0.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect0.cs @@ -285,7 +285,8 @@ namespace FreeSql /// Oracle: for update nowait /// Sqlite: 无效果 /// 达梦: for update nowait - /// 人大金仓: for update nowait + /// 人大金仓: for update nowait + /// 神通: for update /// /// noawait /// diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 88b3bdf2..62b9784d 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -1214,6 +1214,7 @@ namespace FreeSql.Internal.CommonProvider break; case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: + case DataType.OdbcKingbaseES: _tosqlAppendContent = $" for update{(noawait ? " nowait" : "")}"; break; case DataType.Oracle: @@ -1224,8 +1225,8 @@ namespace FreeSql.Internal.CommonProvider break; case DataType.Sqlite: break; - case DataType.OdbcKingbaseES: - _tosqlAppendContent = $" for update{(noawait ? " nowait" : "")}"; + case DataType.ShenTong: //神通测试中发现,不支持 nowait + _tosqlAppendContent = " for update"; break; } return this as TSelect; diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 5c036e53..b45f3d28 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -244,6 +244,7 @@ namespace FreeSql.Internal case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.OdbcKingbaseES: + case DataType.ShenTong: if (strlen < 0) colattr.DbType = "TEXT"; else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); break; diff --git a/Providers/FreeSql.Provider.ShenTong/Curd/OnConflictDoUpdate.cs b/Providers/FreeSql.Provider.ShenTong/Curd/OnConflictDoUpdate.cs new file mode 100644 index 00000000..7857f92f --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/Curd/OnConflictDoUpdate.cs @@ -0,0 +1,207 @@ +using FreeSql.Aop; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.ShenTong.Curd +{ + public class OnConflictDoUpdate where T1 : class + { + internal ShenTongInsert _oscarInsert; + internal ShenTongUpdate _oscarUpdatePriv; + internal ShenTongUpdate _oscarUpdate => _oscarUpdatePriv ?? + (_oscarUpdatePriv = new ShenTongUpdate(_oscarInsert.InternalOrm, _oscarInsert.InternalCommonUtils, _oscarInsert.InternalCommonExpression, null) { InternalTableAlias = "EXCLUDED" } + .NoneParameter().SetSource(_oscarInsert.InternalSource) as ShenTongUpdate); + ColumnInfo[] _columns; + bool _doNothing; + + public OnConflictDoUpdate(IInsert insert, Expression> columns = null) + { + _oscarInsert = insert as ShenTongInsert; + if (_oscarInsert == null) throw new Exception("OnConflictDoUpdate 是 FreeSql.Provider.ShenTong 特有的功能"); + + if (columns != null) + { + var colsList = new List(); + var cols = _oscarInsert.InternalCommonExpression.ExpressionSelectColumns_MemberAccess_New_NewArrayInit(null, columns?.Body, false, null).ToDictionary(a => a, a => true); + foreach (var col in _oscarInsert.InternalTable.Columns.Values) + if (cols.ContainsKey(col.Attribute.Name)) + colsList.Add(col); + _columns = colsList.ToArray(); + } + if (_columns == null || _columns.Any() == false) + _columns = _oscarInsert.InternalTable.Primarys; + if (_columns.Any() == false) throw new Exception("OnConflictDoUpdate 功能要求实体类必须设置 IsPrimary 属性"); + } + + protected void ClearData() + { + _oscarInsert.InternalClearData(); + _oscarUpdatePriv = null; + } + + public OnConflictDoUpdate IgnoreColumns(Expression> columns) + { + _oscarUpdate.IgnoreColumns(columns); + return this; + } + public OnConflictDoUpdate UpdateColumns(Expression> columns) + { + _oscarUpdate.UpdateColumns(columns); + return this; + } + public OnConflictDoUpdate IgnoreColumns(string[] columns) + { + _oscarUpdate.IgnoreColumns(columns); + return this; + } + public OnConflictDoUpdate UpdateColumns(string[] columns) + { + _oscarUpdate.UpdateColumns(columns); + return this; + } + + public OnConflictDoUpdate Set(Expression> column, TMember value) + { + _oscarUpdate.Set(column, value); + return this; + } + //由于表达式解析问题,ON CONFLICT("id") DO UPDATE SET 需要指定表别名,如 Set(a => a.Clicks + 1) 解析会失败 + //暂时不开放这个功能,如有需要使用 SetRaw("click = t.click + 1") 替代该操作 + //public OnConflictDoUpdate Set(Expression> exp) + //{ + // _oscarUpdate.Set(exp); + // return this; + //} + public OnConflictDoUpdate SetRaw(string sql) + { + _oscarUpdate.SetRaw(sql); + return this; + } + + public OnConflictDoUpdate DoNothing() + { + _doNothing = true; + return this; + } + + public string ToSql() + { + var sb = new StringBuilder(); + sb.Append(_oscarInsert.ToSql()).Append("\r\nON CONFLICT("); + for (var a = 0; a < _columns.Length; a++) + { + if (a > 0) sb.Append(", "); + sb.Append(_oscarInsert.InternalCommonUtils.QuoteSqlName(_columns[a].Attribute.Name)); + } + if (_doNothing) + { + sb.Append(") DO NOTHING"); + } + else + { + sb.Append(") DO UPDATE SET\r\n"); + + var sbSetEmpty = _oscarUpdate.InternalSbSet.Length == 0; + var sbSetIncrEmpty = _oscarUpdate.InternalSbSetIncr.Length == 0; + if (sbSetEmpty == false || sbSetIncrEmpty == false) + { + if (sbSetEmpty == false) sb.Append(_oscarUpdate.InternalSbSet.ToString().Substring(2)); + if (sbSetIncrEmpty == false) sb.Append(sbSetEmpty ? _oscarUpdate.InternalSbSetIncr.ToString().Substring(2) : _oscarUpdate.InternalSbSetIncr.ToString()); + } + else + { + var colidx = 0; + foreach (var col in _oscarInsert.InternalTable.Columns.Values) + { + if (col.Attribute.IsPrimary || _oscarUpdate.InternalIgnore.ContainsKey(col.Attribute.Name)) continue; + + if (colidx > 0) sb.Append(", \r\n"); + + if (col.Attribute.IsVersion == true) + { + var field = _oscarInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name); + sb.Append(field).Append(" = ").Append(_oscarInsert.InternalCommonUtils.QuoteSqlName(_oscarInsert.InternalTable.DbName)).Append(".").Append(field).Append(" + 1"); + } + else if (_oscarInsert.InternalIgnore.ContainsKey(col.Attribute.Name)) + { + var caseWhen = _oscarUpdate.InternalWhereCaseSource(col.CsName, sqlval => sqlval).Trim(); + sb.Append(caseWhen); + if (caseWhen.EndsWith(" END")) _oscarUpdate.InternalToSqlCaseWhenEnd(sb, col); + } + else + { + var field = _oscarInsert.InternalCommonUtils.QuoteSqlName(col.Attribute.Name); + sb.Append(field).Append(" = EXCLUDED.").Append(field); + } + ++colidx; + } + } + } + + return sb.ToString(); + } + + public long ExecuteAffrows() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new CurdBeforeEventArgs(_oscarInsert.InternalTable.Type, _oscarInsert.InternalTable, CurdType.Insert, sql, _oscarInsert.InternalParams); + _oscarInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_oscarInsert, before); + long ret = 0; + Exception exception = null; + try + { + ret = _oscarInsert.InternalOrm.Ado.ExecuteNonQuery(_oscarInsert.InternalConnection, _oscarInsert.InternalTransaction, CommandType.Text, sql, _oscarInsert.InternalParams); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new CurdAfterEventArgs(before, exception, ret); + _oscarInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_oscarInsert, after); + ClearData(); + } + return ret; + } + +#if net40 +#else + async public Task ExecuteAffrowsAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + var before = new CurdBeforeEventArgs(_oscarInsert.InternalTable.Type, _oscarInsert.InternalTable, CurdType.Insert, sql, _oscarInsert.InternalParams); + _oscarInsert.InternalOrm.Aop.CurdBeforeHandler?.Invoke(_oscarInsert, before); + long ret = 0; + Exception exception = null; + try + { + ret = await _oscarInsert.InternalOrm.Ado.ExecuteNonQueryAsync(_oscarInsert.InternalConnection, _oscarInsert.InternalTransaction, CommandType.Text, sql, _oscarInsert.InternalParams); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new CurdAfterEventArgs(before, exception, ret); + _oscarInsert.InternalOrm.Aop.CurdAfterHandler?.Invoke(_oscarInsert, after); + ClearData(); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongDelete.cs b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongDelete.cs new file mode 100644 index 00000000..154af784 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongDelete.cs @@ -0,0 +1,99 @@ +using FreeSql.Internal; +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.ShenTong.Curd +{ + + class ShenTongDelete : Internal.CommonProvider.DeleteProvider where T1 : class + { + public ShenTongDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) + : base(orm, commonUtils, commonExpression, dywhere) + { + } + + public override List ExecuteDeleted() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return new List(); + + var sb = new StringBuilder(); + sb.Append(sql).Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sb.Append(", "); + sb.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + sql = sb.ToString(); + var dbParms = _params.ToArray(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + ret = _orm.Ado.Query(_connection, _transaction, CommandType.Text, sql, dbParms); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + this.ClearData(); + return ret; + } + +#if net40 +#else + async public override Task> ExecuteDeletedAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return new List(); + + var sb = new StringBuilder(); + sb.Append(sql).Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sb.Append(", "); + sb.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + sql = sb.ToString(); + var dbParms = _params.ToArray(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + ret = await _orm.Ado.QueryAsync(_connection, _transaction, CommandType.Text, sql, dbParms); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + this.ClearData(); + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsert.cs b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsert.cs new file mode 100644 index 00000000..4c3c144b --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsert.cs @@ -0,0 +1,216 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.ShenTong.Curd +{ + + class ShenTongInsert : Internal.CommonProvider.InsertProvider where T1 : class + { + public ShenTongInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + internal IFreeSql InternalOrm => _orm; + internal TableInfo InternalTable => _table; + internal DbParameter[] InternalParams => _params; + internal DbConnection InternalConnection => _connection; + internal DbTransaction InternalTransaction => _transaction; + internal CommonUtils InternalCommonUtils => _commonUtils; + internal CommonExpression InternalCommonExpression => _commonExpression; + internal List InternalSource => _source; + internal Dictionary InternalIgnore => _ignore; + internal void InternalClearData() => ClearData(); + + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 206); + public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 206); + public override List ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 206); + + protected override long RawExecuteIdentity() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + long ret = 0; + Exception exception = null; + Aop.CurdBeforeEventArgs before = null; + + var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true); + if (identCols.Any() == false) + { + before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + try + { + ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _params); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return 0; + } + sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)); + before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + try + { + long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _params)), out ret); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + + protected override List RawExecuteInserted() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return new List(); + + var sb = new StringBuilder(); + sb.Append(sql).Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sb.Append(", "); + sb.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + sql = sb.ToString(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + ret = _orm.Ado.Query(_connection, _transaction, CommandType.Text, sql, _params); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + +#if net40 +#else + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + public override Task ExecuteIdentityAsync() => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + public override Task> ExecuteInsertedAsync() => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + + async protected override Task RawExecuteIdentityAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + long ret = 0; + Exception exception = null; + Aop.CurdBeforeEventArgs before = null; + + var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true); + if (identCols.Any() == false) + { + before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + try + { + ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _params); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return 0; + } + sql = string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)); + before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + try + { + long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _params)), out ret); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + async protected override Task> RawExecuteInsertedAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return new List(); + + var sb = new StringBuilder(); + sb.Append(sql).Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sb.Append(", "); + sb.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + sql = sb.ToString(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + ret = await _orm.Ado.QueryAsync(_connection, _transaction, CommandType.Text, sql, _params); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsertOrUpdate.cs b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsertOrUpdate.cs new file mode 100644 index 00000000..f3ec0b2d --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongInsertOrUpdate.cs @@ -0,0 +1,72 @@ +using FreeSql.Internal; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Text; + +namespace FreeSql.ShenTong.Curd +{ + + class ShenTongInsertOrUpdate : Internal.CommonProvider.InsertOrUpdateProvider where T1 : class + { + public ShenTongInsertOrUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + } + + public override string ToSql() + { + if (_source?.Any() != true) return null; + + var sqls = new string[2]; + var dbParams = new List(); + var ds = SplitSourceByIdentityValueIsNull(_source); + if (ds.Item1.Any()) sqls[0] = getMergeSql(ds.Item1); + if (ds.Item2.Any()) sqls[1] = getInsertSql(ds.Item2); + _params = dbParams.ToArray(); + if (ds.Item2.Any() == false) return sqls[0]; + if (ds.Item1.Any() == false) return sqls[1]; + return string.Join("\r\n\r\n;\r\n\r\n", sqls); + + string getMergeSql(List data) + { + if (_table.Primarys.Any() == false) throw new Exception($"InsertOrUpdate 功能执行 merge into 要求实体类 {_table.CsName} 必须有主键"); + + var sb = new StringBuilder().Append("MERGE INTO ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" t1 \r\nUSING ("); + WriteSourceSelectUnionAll(data, sb, dbParams); + sb.Append(" ) t2 ON (").Append(string.Join(" AND ", _table.Primarys.Select(a => $"t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}"))).Append(") \r\n"); + + var cols = _table.Columns.Values.Where(a => a.Attribute.IsPrimary == false && a.Attribute.CanUpdate == true); + if (cols.Any()) + sb.Append("WHEN MATCHED THEN \r\n") + .Append(" update set ").Append(string.Join(", ", cols.Select(a => + a.Attribute.IsVersion ? + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t1.{_commonUtils.QuoteSqlName(a.Attribute.Name)} + 1" : + $"{_commonUtils.QuoteSqlName(a.Attribute.Name)} = t2.{a.Attribute.Name}" + ))).Append(" \r\n"); + + cols = _table.Columns.Values.Where(a => a.Attribute.CanInsert == true); + if (cols.Any()) + sb.Append("WHEN NOT MATCHED THEN \r\n") + .Append(" insert (").Append(string.Join(", ", cols.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))).Append(") \r\n") + .Append(" values (").Append(string.Join(", ", cols.Select(a => $"t2.{a.Attribute.Name}"))).Append(")"); + + return sb.ToString(); + } + string getInsertSql(List data) + { + var insert = _orm.Insert() + .AsTable(_tableRule).AsType(_table.Type) + .WithConnection(_connection) + .WithTransaction(_transaction) + .NoneParameter(true) as Internal.CommonProvider.InsertProvider; + insert._source = data; + var sql = insert.ToSql(); + if (string.IsNullOrEmpty(sql)) return null; + if (insert._params?.Any() == true) dbParams.AddRange(insert._params); + return sql; + } + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongSelect.cs b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongSelect.cs new file mode 100644 index 00000000..f3393d8e --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongSelect.cs @@ -0,0 +1,174 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace FreeSql.ShenTong.Curd +{ + + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select1Provider where T1 : class + { + + internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List _tables, List> tbUnions, Func _aliasRule, string _tosqlAppendContent, List _whereCascadeExpression, IFreeSql _orm) + { + if (_orm.CodeFirst.IsAutoSyncStructure) + _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray()); + + if (_whereCascadeExpression.Any()) + foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent)) + tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereCascadeExpression, true); + + var sb = new StringBuilder(); + var tbUnionsGt0 = tbUnions.Count > 1; + for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++) + { + if (tbUnionsIdx > 0) sb.Append(" \r\n\r\nUNION ALL\r\n\r\n"); + if (tbUnionsGt0) sb.Append(_select).Append(" * from ("); + var tbUnion = tbUnions[tbUnionsIdx]; + + var sbnav = new StringBuilder(); + sb.Append(_select); + if (_distinct) sb.Append("DISTINCT "); + sb.Append(field).Append(" \r\nFROM "); + var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray(); + var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray(); + for (var a = 0; a < tbsfrom.Length; a++) + { + sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias); + if (tbsjoin.Length > 0) + { + //如果存在 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); + + if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1"); + else + { + var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On; + sb.Append(" ON ").Append(onSql); + if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false) + { + if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade); + else sb.Append(" AND (").Append(tbsfrom[b].Cascade).Append(")"); + } + } + } + break; + } + else + { + if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")"); + if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")"); + if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND (").Append(tbsfrom[a].Cascade).Append(")"); + } + if (a < tbsfrom.Length - 1) sb.Append(", "); + } + foreach (var tb in tbsjoin) + { + if (tb.Type == SelectTableInfoType.Parent) continue; + switch (tb.Type) + { + case SelectTableInfoType.LeftJoin: + sb.Append(" \r\nLEFT JOIN "); + break; + case SelectTableInfoType.InnerJoin: + sb.Append(" \r\nINNER JOIN "); + break; + case SelectTableInfoType.RightJoin: + sb.Append(" \r\nRIGHT 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); + if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND (").Append(tb.Cascade).Append(")"); + if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")"); + } + if (_join.Length > 0) sb.Append(_join); + + sbnav.Append(_where); + if (!string.IsNullOrEmpty(_tables[0].Cascade)) + sbnav.Append(" AND (").Append(_tables[0].Cascade).Append(")"); + + if (sbnav.Length > 0) + { + sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5)); + } + if (string.IsNullOrEmpty(_groupby) == false) + { + sb.Append(_groupby); + if (string.IsNullOrEmpty(_having) == false) + sb.Append(" \r\nHAVING ").Append(_having.Substring(5)); + } + sb.Append(_orderby); + if (_limit > 0) + sb.Append(" \r\nlimit ").Append(_limit); + if (_skip > 0) + sb.Append(" \r\noffset ").Append(_skip); + + sbnav.Clear(); + if (tbUnionsGt0) sb.Append(") ftb"); + } + return sb.Append(_tosqlAppendContent).ToString(); + } + + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override ISelect From(Expression, T2, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new ShenTongSelect(_orm, _commonUtils, _commonExpression, null); ShenTongSelect.CopyData(this, ret, exp?.Parameters); return ret; } + public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select2Provider where T1 : class where T2 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select3Provider where T1 : class where T2 : class where T3 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select4Provider where T1 : class where T2 : class where T3 : class where T4 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select5Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select6Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select7Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select8Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select9Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } + class ShenTongSelect : FreeSql.Internal.CommonProvider.Select10Provider where T1 : class where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class + { + public ShenTongSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => ShenTongSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereCascadeExpression, _orm); + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongUpdate.cs b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongUpdate.cs new file mode 100644 index 00000000..28baa171 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/Curd/ShenTongUpdate.cs @@ -0,0 +1,164 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FreeSql.ShenTong.Curd +{ + + class ShenTongUpdate : Internal.CommonProvider.UpdateProvider where T1 : class + { + + public ShenTongUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) + : base(orm, commonUtils, commonExpression, dywhere) + { + } + + internal string InternalTableAlias; + internal StringBuilder InternalSbSet => _set; + internal StringBuilder InternalSbSetIncr => _setIncr; + internal Dictionary InternalIgnore => _ignore; + internal void InternalResetSource(List source) => _source = source; + internal string InternalWhereCaseSource(string CsName, Func thenValue) => WhereCaseSource(CsName, thenValue); + internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col); + + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 206); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 206); + + protected override List RawExecuteUpdated() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return new List(); + + var sb = new StringBuilder(); + sb.Append(sql).Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sb.Append(", "); + sb.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + sql = sb.ToString(); + var dbParms = _params.Concat(_paramsSource).ToArray(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + ret = _orm.Ado.Query(_connection, _transaction, CommandType.Text, sql, dbParms); + ValidateVersionAndThrow(ret.Count); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + + protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) + { + if (_table.Primarys.Length == 1) + { + var pk = _table.Primarys.First(); + if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append("."); + caseWhen.Append(_commonUtils.QuoteReadColumn(pk.CsType, pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))); + return; + } + caseWhen.Append("("); + var pkidx = 0; + foreach (var pk in _table.Primarys) + { + if (pkidx > 0) caseWhen.Append(" || '+' || "); + if (string.IsNullOrEmpty(InternalTableAlias) == false) caseWhen.Append(InternalTableAlias).Append("."); + caseWhen.Append(_commonUtils.QuoteReadColumn(pk.CsType, pk.Attribute.MapType, _commonUtils.QuoteSqlName(pk.Attribute.Name))).Append("::varchar"); + ++pkidx; + } + caseWhen.Append(")"); + } + + protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) + { + if (_table.Primarys.Length == 1) + { + sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys.First().GetMapValue(d))); + return; + } + sb.Append("("); + var pkidx = 0; + foreach (var pk in _table.Primarys) + { + if (pkidx > 0) sb.Append(" || '+' || "); + sb.Append(_commonUtils.FormatSql("{0}", pk.GetMapValue(d))).Append("::varchar"); + ++pkidx; + } + sb.Append(")"); + } + + protected override void ToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) + { + if (_noneParameter == false) return; + var dbtype = _commonUtils.CodeFirst.GetDbInfo(col.Attribute.MapType)?.dbtype; + if (dbtype == null) return; + + sb.Append("::").Append(dbtype); + } + +#if net40 +#else + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + public override Task> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000); + + async protected override Task> RawExecuteUpdatedAsync() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return new List(); + + var sb = new StringBuilder(); + sb.Append(sql).Append(" RETURNING "); + + var colidx = 0; + foreach (var col in _table.Columns.Values) + { + if (colidx > 0) sb.Append(", "); + sb.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); + ++colidx; + } + sql = sb.ToString(); + var dbParms = _params.Concat(_paramsSource).ToArray(); + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Update, sql, dbParms); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + var ret = new List(); + Exception exception = null; + try + { + ret = await _orm.Ado.QueryAsync(_connection, _transaction, CommandType.Text, sql, dbParms); + ValidateVersionAndThrow(ret.Count); + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj b/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj new file mode 100644 index 00000000..75de475b --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj @@ -0,0 +1,52 @@ + + + + netstandard2.0;net45;net40 + 1.6.0-preview0602 + true + ncc;YeXiangQin + FreeSql 数据库实现,基于 神州通用数据库 7.0.8 + https://github.com/2881099/FreeSql + https://github.com/2881099/FreeSql + git + MIT + FreeSql;ORM;ShenTong;Oscar;神通;神州通用 + $(AssemblyName) + logo.png + $(AssemblyName) + true + true + + + + + + + + + + + + Always + + + + + + + + + + lib\System.Data.OscarClient.dll + false + + + + + ns20;netstandard20 + + + net40 + + + diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongAdo.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongAdo.cs new file mode 100644 index 00000000..fee92115 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongAdo.cs @@ -0,0 +1,77 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using FreeSql.Internal.ObjectPool; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data.Common; +using System.Data.OscarClient; +using System.Text; +using System.Threading; + +namespace FreeSql.ShenTong +{ + class ShenTongAdo : FreeSql.Internal.CommonProvider.AdoProvider + { + public ShenTongAdo() : base(DataType.ShenTong, null, null) { } + public ShenTongAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func connectionFactory) : base(DataType.ShenTong, masterConnectionString, slaveConnectionStrings) + { + base._util = util; + if (connectionFactory != null) + { + MasterPool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.ShenTong, connectionFactory); + return; + } + if (!string.IsNullOrEmpty(masterConnectionString)) + MasterPool = new ShenTongConnectionPool("主库", masterConnectionString, null, null); + if (slaveConnectionStrings != null) + { + foreach (var slaveConnectionString in slaveConnectionStrings) + { + var slavePool = new ShenTongConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables)); + SlavePools.Add(slavePool); + } + } + } + + public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn) + { + if (param == null) return "NULL"; + if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false)) + param = Utils.GetDataReaderValue(mapType, param); + + if (param is bool || param is bool?) + return (bool)param ? "'t'" : "'f'"; + else if (param is string || param is char) + return string.Concat("'", param.ToString().Replace("'", "''"), "'"); + else if (param is Enum) + return ((Enum)param).ToInt64(); + else if (decimal.TryParse(string.Concat(param), out var trydec)) + return param; + else if (param is DateTime || param is DateTime?) + return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'"); + else if (param is TimeSpan || param is TimeSpan?) + return ((TimeSpan)param).TotalSeconds; + else if (param is byte[]) + return $"0x{CommonUtils.BytesSqlRaw(param as byte[])}"; + else if (param is IEnumerable) + return AddslashesIEnumerable(param, mapType, mapColumn); + + return string.Concat("'", param.ToString().Replace("'", "''"), "'"); + } + + protected override DbCommand CreateCommand() + { + return new OscarCommand(); + } + + protected override void ReturnConnection(IObjectPool pool, Object conn, Exception ex) + { + var rawPool = pool as ShenTongConnectionPool; + if (rawPool != null) rawPool.Return(conn, ex); + else pool.Return(conn); + } + + protected override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj); + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongConnectionPool.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongConnectionPool.cs new file mode 100644 index 00000000..43c1766b --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongAdo/ShenTongConnectionPool.cs @@ -0,0 +1,247 @@ +using FreeSql.Internal.ObjectPool; +using System; +using System.Collections.Concurrent; +using System.Data; +using System.Data.Common; +using System.Data.OscarClient; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace FreeSql.ShenTong +{ + + class ShenTongConnectionPool : ObjectPool + { + + internal Action availableHandler; + internal Action unavailableHandler; + + public ShenTongConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null) + { + this.availableHandler = availableHandler; + this.unavailableHandler = unavailableHandler; + var policy = new ShenTongConnectionPoolPolicy + { + _pool = this, + Name = name + }; + this.Policy = policy; + policy.ConnectionString = connectionString; + } + + public void Return(Object obj, Exception exception, bool isRecreate = false) + { + if (exception != null && exception is OscarException) + { + + if (exception is System.IO.IOException) + { + + base.SetUnavailable(exception); + + } + else if (obj.Value.Ping() == false) + { + + base.SetUnavailable(exception); + } + } + base.Return(obj, isRecreate); + } + } + + class ShenTongConnectionPoolPolicy : IPolicy + { + + internal ShenTongConnectionPool _pool; + public string Name { get; set; } = "ShenTong OscarConnection 对象池"; + public int PoolSize { get; set; } = 50; + public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10); + public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(20); + public int AsyncGetCapacity { get; set; } = 10000; + public bool IsThrowGetTimeoutException { get; set; } = true; + public bool IsAutoDisposeWithSystem { get; set; } = true; + public int CheckAvailableInterval { get; set; } = 5; + + static ConcurrentDictionary dicConnStrIncr = new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase); + private string _connectionString; + public string ConnectionString + { + get => _connectionString; + set + { + _connectionString = value ?? ""; + + var pattern = @"Max(imum)?\s*pool\s*size\s*=\s*(\d+)"; + Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase); + if (m.Success == false || int.TryParse(m.Groups[2].Value, out var poolsize) == false || poolsize <= 0) poolsize = 50; + var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => Math.Min(5, oldval + 1)); + PoolSize = poolsize + connStrIncr; + _connectionString = m.Success ? + Regex.Replace(_connectionString, pattern, $"MAXPOOLSIZE={PoolSize}", RegexOptions.IgnoreCase) : + $"{_connectionString};MAXPOOLSIZE={PoolSize}"; + + pattern = @"Connection\s*LifeTime\s*=\s*(\d+)"; + m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase); + if (m.Success) + { + IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value)); + _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase); + } + + var minPoolSize = 0; + pattern = @"Min(imum)?\s*pool\s*size\s*=\s*(\d+)"; + m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase); + if (m.Success) + { + minPoolSize = int.Parse(m.Groups[2].Value); + _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase); + } + + FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize); + } + } + + public bool OnCheckAvailable(Object obj) + { + if (obj.Value.State == ConnectionState.Closed) obj.Value.Open(); + return obj.Value.Ping(true); + } + + public DbConnection OnCreate() + { + var conn = new OscarConnection(_connectionString); + return conn; + } + + public void OnDestroy(DbConnection obj) + { + try { if (obj.State != ConnectionState.Closed) obj.Close(); } catch { } + try { (obj as OscarConnection)?.ClearPool(); } catch { } + obj.Dispose(); + } + + public void OnGet(Object obj) + { + + if (_pool.IsAvailable) + { + if (obj.Value == null) + { + if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true) + throw new Exception($"【{this.Name}】连接字符串错误,请检查。"); + return; + } + + if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false) + { + + try + { + obj.Value.Open(); + } + catch (Exception ex) + { + if (_pool.SetUnavailable(ex) == true) + throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}"); + } + } + } + } + +#if net40 +#else + async public Task OnGetAsync(Object obj) + { + + if (_pool.IsAvailable) + { + if (obj.Value == null) + { + if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true) + throw new Exception($"【{this.Name}】连接字符串错误,请检查。"); + return; + } + + if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (await obj.Value.PingAsync()) == false) + { + + try + { + await obj.Value.OpenAsync(); + } + catch (Exception ex) + { + if (_pool.SetUnavailable(ex) == true) + throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}"); + } + } + } + } +#endif + + public void OnGetTimeout() + { + + } + + public void OnReturn(Object obj) + { + //if (obj?.Value != null && obj.Value.State != ConnectionState.Closed) try { obj.Value.Close(); } catch { } + } + + public void OnAvailable() + { + _pool.availableHandler?.Invoke(); + } + + public void OnUnavailable() + { + _pool.unavailableHandler?.Invoke(); + } + } + + static class DbConnectionExtensions + { + + static DbCommand PingCommand(DbConnection conn) + { + var cmd = conn.CreateCommand(); + cmd.CommandTimeout = 5; + cmd.CommandText = "select 1"; + return cmd; + } + public static bool Ping(this DbConnection that, bool isThrow = false) + { + try + { + PingCommand(that).ExecuteNonQuery(); + return true; + } + catch + { + if (that.State != ConnectionState.Closed) try { that.Close(); } catch { } + if (isThrow) throw; + return false; + } + } + +#if net40 +#else + async public static Task PingAsync(this DbConnection that, bool isThrow = false) + { + try + { + await PingCommand(that).ExecuteNonQueryAsync(); + return true; + } + catch + { + if (that.State != ConnectionState.Closed) try { that.Close(); } catch { } + if (isThrow) throw; + return false; + } + } +#endif + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongCodeFirst.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongCodeFirst.cs new file mode 100644 index 00000000..7d5d6fd3 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongCodeFirst.cs @@ -0,0 +1,412 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.OscarClient; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Text; +using System.Text.RegularExpressions; + +namespace FreeSql.ShenTong +{ + + class ShenTongCodeFirst : Internal.CommonProvider.CodeFirstProvider + { + + public ShenTongCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { } + + static object _dicCsToDbLock = new object(); + static Dictionary> _dicCsToDb = new Dictionary>() { + + { typeof(sbyte).FullName, CsToDb.New(OscarDbType.SmallInt, "int2","int2 NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(OscarDbType.SmallInt, "int2", "int2", false, true, null) }, + { typeof(short).FullName, CsToDb.New(OscarDbType.SmallInt, "int2","int2 NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(OscarDbType.SmallInt, "int2", "int2", false, true, null) }, + { typeof(int).FullName, CsToDb.New(OscarDbType.Integer, "int4","int4 NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(OscarDbType.Integer, "int4", "int4", false, true, null) }, + { typeof(long).FullName, CsToDb.New(OscarDbType.BigInt, "int8","int8 NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(OscarDbType.BigInt, "int8", "int8", false, true, null) }, + + { typeof(byte).FullName, CsToDb.New(OscarDbType.SmallInt, "int2","int2 NOT NULL", false, false, 0) },{ typeof(byte?).FullName, CsToDb.New(OscarDbType.SmallInt, "int2", "int2", false, true, null) }, + { typeof(ushort).FullName, CsToDb.New(OscarDbType.Integer, "int4","int4 NOT NULL", false, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(OscarDbType.Integer, "int4", "int4", false, true, null) }, + { typeof(uint).FullName, CsToDb.New(OscarDbType.BigInt, "int8","int8 NOT NULL", false, false, 0) },{ typeof(uint?).FullName, CsToDb.New(OscarDbType.BigInt, "int8", "int8", false, true, null) }, + { typeof(ulong).FullName, CsToDb.New(OscarDbType.Numeric, "numeric","numeric(20,0) NOT NULL", false, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(OscarDbType.Numeric, "numeric", "numeric(20,0)", false, true, null) }, + + { typeof(float).FullName, CsToDb.New(OscarDbType.Real, "float4","float4 NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(OscarDbType.Real, "float4", "float4", false, true, null) }, + { typeof(double).FullName, CsToDb.New(OscarDbType.Double, "float8","float8 NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(OscarDbType.Double, "float8", "float8", false, true, null) }, + { typeof(decimal).FullName, CsToDb.New(OscarDbType.Numeric, "numeric", "numeric(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(OscarDbType.Numeric, "numeric", "numeric(10,2)", false, true, null) }, + + { typeof(string).FullName, CsToDb.New(OscarDbType.VarChar, "varchar", "varchar(255)", false, null, "") }, + + { typeof(TimeSpan).FullName, CsToDb.New(OscarDbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(OscarDbType.Time, "time", "time",false, true, null) }, + { typeof(DateTime).FullName, CsToDb.New(OscarDbType.TimeStamp, "timestamp", "timestamp NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(OscarDbType.TimeStamp, "timestamp", "timestamp", false, true, null) }, + + { typeof(bool).FullName, CsToDb.New(OscarDbType.Boolean, "bool","bool NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(OscarDbType.Boolean, "bool","bool", null, true, null) }, + { typeof(Byte[]).FullName, CsToDb.New(OscarDbType.Bytea, "bytea", "bytea", false, null, new byte[0]) }, + { typeof(Guid).FullName, CsToDb.New(OscarDbType.Char, "bpchar", "bpchar(36) NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(OscarDbType.Char, "bpchar", "bpchar(36)", false, true, null) }, + + }; + + public override DbInfoResult GetDbInfo(Type type) + { + var isarray = type.FullName != "System.Byte[]" && type.IsArray; + var elementType = isarray ? type.GetElementType() : type; + var info = GetDbInfoNoneArray(elementType); + if (info == null) return null; + if (isarray == false) return new DbInfoResult((int)info.type, info.dbtype, info.dbtypeFull, info.isnullable, info.defaultValue); + var dbtypefull = Regex.Replace(info.dbtypeFull, $@"{info.dbtype}(\s*\([^\)]+\))?", "$0[]").Replace(" NOT NULL", ""); + return new DbInfoResult((int)(info.type | OscarDbType.Array), $"{info.dbtype}[]", dbtypefull, null, Array.CreateInstance(elementType, 0)); + } + CsToDb GetDbInfoNoneArray(Type type) + { + if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return trydc; + if (type.IsArray) return null; + var enumType = type.IsEnum ? type : null; + if (enumType == null && type.IsNullableType()) + { + var genericTypes = type.GetGenericArguments(); + if (genericTypes.Length == 1 && genericTypes.First().IsEnum) enumType = genericTypes.First(); + } + if (enumType != null) + { + var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ? + CsToDb.New(OscarDbType.BigInt, "int8", $"int8{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()) : + CsToDb.New(OscarDbType.Integer, "int4", $"int4{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()); + if (_dicCsToDb.ContainsKey(type.FullName) == false) + { + lock (_dicCsToDbLock) + { + if (_dicCsToDb.ContainsKey(type.FullName) == false) + _dicCsToDb.Add(type.FullName, newItem); + } + } + return newItem; + } + return null; + } + + protected override string GetComparisonDDLStatements(params TypeAndName[] objects) + { + var sb = new StringBuilder(); + var seqcols = new List>(); //序列 + + foreach (var obj in objects) + { + if (sb.Length > 0) sb.Append("\r\n"); + var tb = _commonUtils.GetTableByEntity(obj.entityType); + if (tb == null) throw new Exception($"类型 {obj.entityType.FullName} 不可迁移"); + if (tb.Columns.Any() == false) throw new Exception($"类型 {obj.entityType.FullName} 不可迁移,可迁移属性0个"); + var tbname = _commonUtils.SplitTableName(tb.DbName); + if (tbname?.Length == 1) tbname = new[] { "PUBLIC", tbname[0] }; + + var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名 + if (tboldname?.Length == 1) tboldname = new[] { "PUBLIC", tboldname[0] }; + if (string.IsNullOrEmpty(obj.tableName) == false) + { + var tbtmpname = _commonUtils.SplitTableName(obj.tableName); + if (tbtmpname?.Length == 1) tbtmpname = new[] { "PUBLIC", tbtmpname[0] }; + if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1]) + { + tbname = tbtmpname; + tboldname = null; + } + } + //codefirst 不支持表名、模式名、数据库名中带 . + + if (string.Compare(tbname[0], "PUBLIC", true) != 0 && _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(" select 1 from sys_namespace where nspname={0}", tbname[0])) == null) //创建模式 + sb.Append("CREATE SCHEMA IF NOT EXISTS ").Append(tbname[0]).Append(";\r\n"); + + var sbalter = new StringBuilder(); + var istmpatler = false; //创建临时表,导入数据,删除旧表,修改 + if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from tables a inner join sys_namespace b on b.nspname = a.table_schem where b.nspname || '.' || a.table_name = '{0}.{1}'", tbname)) == null) + { //表不存在 + if (tboldname != null) + { + if (_orm.Ado.ExecuteScalar(CommandType.Text, string.Format(" select 1 from tables a inner join sys_namespace b on b.nspname = a.table_schem where b.nspname || '.' || a.table_name = '{0}.{1}'", tboldname)) == null) + //旧表不存在 + tboldname = null; + } + if (tboldname == null) + { + //创建表 + var createTableName = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}"); + sb.Append("CREATE TABLE IF NOT EXISTS ").Append(createTableName).Append(" ( "); + foreach (var tbcol in tb.ColumnsByPosition) + { + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(","); + if (tbcol.Attribute.IsIdentity == true) seqcols.Add(NaviteTuple.Create(tbcol, tbname, true)); + } + if (tb.Primarys.Any()) + { + var pkname = $"{tbname[0]}_{tbname[1]}_PKEY"; + sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(pkname)).Append(" PRIMARY KEY ("); + foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); + sb.Remove(sb.Length - 2, 2).Append("),"); + } + sb.Remove(sb.Length - 1, 1); + sb.Append("\r\n) BINLOG ON;\r\n"); + //创建表的索引 + foreach (var uk in tb.Indexes) + { + sb.Append("CREATE "); + if (uk.IsUnique) sb.Append("UNIQUE "); + sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(uk.Name)).Append(" ON ").Append(createTableName).Append("("); + foreach (var tbcol in uk.Columns) + { + sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); + if (tbcol.IsDesc) sb.Append(" DESC"); + sb.Append(", "); + } + sb.Remove(sb.Length - 2, 2).Append(");\r\n"); + } + //备注 + foreach (var tbcol in tb.ColumnsByPosition) + { + if (string.IsNullOrEmpty(tbcol.Comment) == false) + sb.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + } + if (string.IsNullOrEmpty(tb.Comment) == false) + sb.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\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[1]}")).Append(";\r\n"); + else + { + //如果新表,旧表不在一起,创建新表,导入数据,删除旧表 + istmpatler = true; + } + } + else + tboldname = null; //如果新表已经存在,不走改表名逻辑 + + //对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段 + var sql = _commonUtils.FormatSql(@" +select +a.attname, +t.typname, +case when a.atttypmod > 0 and a.atttypmod < 32767 then a.atttypmod - 4 else a.attlen end len, +case when t.typelem > 0 and t.typinput = 'ARRAY_IN' then t2.typname else t.typname end, +case when a.attnotnull then '0' else '1' end as is_nullable, +--e.adsrc, +(select sys_get_expr(adbin, adrelid) from sys_attrdef where adrelid = e.adrelid limit 1) is_identity, +a.attndims, +d.description as comment +from sys_class c +inner join sys_attribute a on a.attnum > 0 and a.attrelid = c.oid +inner join sys_type t on t.oid = a.atttypid +left join sys_type t2 on t2.oid = t.typelem +left join sys_description d on d.objoid = a.attrelid and d.objsubid = a.attnum +left join sys_attrdef e on e.adrelid = a.attrelid and e.adnum = a.attnum +inner join sys_namespace ns on ns.oid = c.relnamespace +inner join sys_namespace ns2 on ns2.oid = t.typnamespace +where ns.nspname = {0} and c.relname = {1}", tboldname ?? tbname); + var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a => + { + var attndims = int.Parse(string.Concat(a[6])); + var type = string.Concat(a[1]); + var sqlType = string.Concat(a[3]); + var max_length = long.Parse(string.Concat(a[2])); + switch (sqlType.ToLower()) + { + case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break; + default: max_length *= 8; break; + } + if (type.StartsWith("_")) + { + type = type.Substring(1); + if (attndims == 0) attndims++; + } + if (sqlType.StartsWith("_")) sqlType = sqlType.Substring(1); + return new + { + column = string.Concat(a[0]), + sqlType = string.Concat(sqlType), + max_length = long.Parse(string.Concat(a[2])), + is_nullable = string.Concat(a[4]) == "1", + is_identity = string.Concat(a[5]).StartsWith(@"NEXTVAL('") && string.Concat(a[5]).EndsWith(@"'::text)"), + attndims, + comment = string.Concat(a[7]) + }; + }, StringComparer.CurrentCultureIgnoreCase); + + if (istmpatler == false) + { + 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 isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? ""); + if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false || + tbcol.Attribute.DbType.Contains("[]") != (tbstructcol.attndims > 0)) + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TYPE ").Append(tbcol.Attribute.DbType.Split(' ').First()).Append(";\r\n"); + if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable) + { + if (tbcol.Attribute.IsNullable != true || tbcol.Attribute.IsNullable == true && tbcol.Attribute.IsPrimary == false) + { + if (tbcol.Attribute.IsNullable == false) + sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" = ").Append(tbcol.DbDefaultValue).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" IS NULL;\r\n"); + sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.IsNullable == true ? "DROP" : "SET").Append(" NOT NULL;\r\n"); + } + } + if (tbcol.Attribute.IsIdentity != tbstructcol.is_identity) + seqcols.Add(NaviteTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true)); + if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0) + //修改列名 + 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"); + if (isCommentChanged) + sbalter.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + continue; + } + //添加列 + 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.Split(' ').First()).Append(";\r\n"); + sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" = ").Append(tbcol.DbDefaultValue).Append(";\r\n"); + if (tbcol.Attribute.IsNullable == false) sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" ALTER COLUMN ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" SET NOT NULL;\r\n"); + if (tbcol.Attribute.IsIdentity == true) seqcols.Add(NaviteTuple.Create(tbcol, tbname, tbcol.Attribute.IsIdentity == true)); + if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + } + var dsuksql = _commonUtils.FormatSql(@" +select +c.attname, +b.relname, +--case when sys_index_column_has_property(b.oid, c.attnum, 'desc') = 't' then 1 else 0 end IsDesc, +0, +case when indisunique = 't' then 1 else 0 end IsUnique +from sys_index a +inner join sys_class b on b.oid = a.indexrelid +inner join sys_attribute c on c.attnum > 0 and c.attrelid = b.oid +inner join sys_namespace ns on ns.oid = b.relnamespace +inner join sys_class d on d.oid = a.indrelid +where ns.nspname in ({0}) and d.relname in ({1}) and a.indisprimary = 'f'", tboldname ?? tbname); + var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]), string.Concat(a[2]), string.Concat(a[3]) }); + foreach (var uk in tb.Indexes) + { + if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) continue; + var dsukfind1 = dsuk.Where(a => string.Compare(a[1], uk.Name, true) == 0).ToArray(); + if (dsukfind1.Any() == false || dsukfind1.Length != uk.Columns.Length || dsukfind1.Where(a => uk.Columns.Where(b => (a[3] == "1") == uk.IsUnique && string.Compare(b.Column.Attribute.Name, a[0], true) == 0 && (a[2] == "1") == b.IsDesc).Any()).Count() != uk.Columns.Length) + { + if (dsukfind1.Any()) sbalter.Append("DROP INDEX ").Append(_commonUtils.QuoteSqlName(uk.Name)).Append(";\r\n"); + sbalter.Append("CREATE "); + if (uk.IsUnique) sbalter.Append("UNIQUE "); + sbalter.Append("INDEX ").Append(_commonUtils.QuoteSqlName(uk.Name)).Append(" ON ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append("("); + foreach (var tbcol in uk.Columns) + { + sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); + if (tbcol.IsDesc) sbalter.Append(" DESC"); + sbalter.Append(", "); + } + sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n"); + } + } + } + if (istmpatler == false) + { + var dbcomment = string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@" select +d.description +from sys_class a +inner join sys_namespace b on b.oid = a.relnamespace +left join sys_description d on d.objoid = a.oid and objsubid = 0 +where b.nspname not in ('DIRECTORIES', 'INFO_SCHEM', 'REPLICATION', 'STAGENT', 'SYSAUDIT', 'SYSDBA', 'SYSFTSDBA', 'SYSSECURE', 'SYS_GLOBAL_TEMP', 'WMSYS') and a.relkind in ('r') and b.nspname = {0} and a.relname = {1} +and b.nspname || '.' || a.relname not in ('PUBLIC.GEOGRAPHY_COLUMNS','PUBLIC.GEOMETRY_COLUMNS','PUBLIC.RASTER_COLUMNS','PUBLIC.RASTER_OVERVIEWS')", tbname[0], tbname[1]))); + if (dbcomment != (tb.Comment ?? "")) + sb.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\r\n"); + + sb.Append(sbalter); + continue; + } + var oldpk = _orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@" select sys_constraint.conname as pk_name from sys_constraint +inner join sys_class on sys_constraint.conrelid = sys_class.oid +inner join sys_namespace on sys_namespace.oid = sys_class.relnamespace +where sys_namespace.nspname={0} and sys_class.relname={1} and sys_constraint.contype='p' +", tbname))?.ToString(); + if (string.IsNullOrEmpty(oldpk) == false) + sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" DROP CONSTRAINT ").Append(oldpk).Append(";\r\n"); + + //创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名 + var tablename = tboldname == null ? _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}") : _commonUtils.QuoteSqlName($"{tboldname[0]}.{tboldname[1]}"); + var tmptablename = _commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}"); + //创建临时表 + sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( "); + foreach (var tbcol in tb.ColumnsByPosition) + { + sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType).Append(","); + if (tbcol.Attribute.IsIdentity == true) seqcols.Add(NaviteTuple.Create(tbcol, tbname, true)); + } + if (tb.Primarys.Any()) + { + var pkname = $"{tbname[0]}_{tbname[1]}_pkey"; + sb.Append(" \r\n CONSTRAINT ").Append(_commonUtils.QuoteSqlName(pkname)).Append(" PRIMARY KEY ("); + foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); + sb.Remove(sb.Length - 2, 2).Append("),"); + } + sb.Remove(sb.Length - 1, 1); + sb.Append("\r\n) BINLOG ON;\r\n"); + //备注 + foreach (var tbcol in tb.ColumnsByPosition) + { + if (string.IsNullOrEmpty(tbcol.Comment) == false) + sb.Append("COMMENT ON COLUMN ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}.{tbcol.Attribute.Name}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment)).Append(";\r\n"); + } + if (string.IsNullOrEmpty(tb.Comment) == false) + sb.Append("COMMENT ON TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.FTmp_{tbname[1]}")).Append(" IS ").Append(_commonUtils.FormatSql("{0}", tb.Comment)).Append(";\r\n"); + + sb.Append("INSERT INTO ").Append(tmptablename).Append(" ("); + foreach (var tbcol in tb.ColumnsByPosition) + sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", "); + sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT "); + foreach (var tbcol in tb.ColumnsByPosition) + { + 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)) + { + insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column); + 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 = $"coalesce({insertvalue},{tbcol.DbDefaultValue})"; + } + else if (tbcol.Attribute.IsNullable == false) + 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("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[1])).Append(";\r\n"); + //创建表的索引 + foreach (var uk in tb.Indexes) + { + sb.Append("CREATE "); + if (uk.IsUnique) sb.Append("UNIQUE "); + sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(uk.Name)).Append(" ON ").Append(tablename).Append("("); + foreach (var tbcol in uk.Columns) + { + sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); + if (tbcol.IsDesc) sb.Append(" DESC"); + sb.Append(", "); + } + sb.Remove(sb.Length - 2, 2).Append(");\r\n"); + } + } + foreach (var seqcol in seqcols) + { + var tbname = seqcol.Item2; + var seqname = Utils.GetCsName($"{tbname[0]}.{tbname[1]}_{seqcol.Item1.Attribute.Name}_seq").ToUpper(); ; + var tbname2 = _commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}"); + var colname2 = _commonUtils.QuoteSqlName(seqcol.Item1.Attribute.Name); + sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT null;\r\n"); + sb.Append("DROP SEQUENCE IF EXISTS ").Append(seqname).Append(";\r\n"); + if (seqcol.Item3) + { + sb.Append("CREATE SEQUENCE ").Append(seqname).Append(";\r\n"); + sb.Append("ALTER TABLE ").Append(tbname2).Append(" ALTER COLUMN ").Append(colname2).Append(" SET DEFAULT NEXTVAL('").Append(seqname).Append("'::text);\r\n"); + sb.Append(" SELECT case when max(").Append(colname2).Append(") is null then 0 else setval('").Append(seqname).Append("', max(").Append(colname2).Append(")) end FROM ").Append(tbname2).Append(";\r\n"); + } + } + return sb.Length == 0 ? null : sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongDbFirst.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongDbFirst.cs new file mode 100644 index 00000000..dcea104e --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongDbFirst.cs @@ -0,0 +1,513 @@ +using FreeSql.DatabaseModel; +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.OscarClient; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Text; +using System.Text.RegularExpressions; + +namespace FreeSql.ShenTong +{ + class ShenTongDbFirst : IDbFirst + { + IFreeSql _orm; + protected CommonUtils _commonUtils; + protected CommonExpression _commonExpression; + public ShenTongDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + { + _orm = orm; + _commonUtils = commonUtils; + _commonExpression = commonExpression; + } + + public int GetDbType(DbColumnInfo column) => (int)GetOscarDbType(column); + OscarDbType GetOscarDbType(DbColumnInfo column) + { + var dbtype = column.DbTypeText; + var isarray = dbtype.EndsWith("[]"); + if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2); + OscarDbType ret = OscarDbType.Oidvector; + switch (dbtype.ToLower().TrimStart('_')) + { + case "int2": ret = OscarDbType.SmallInt; break; + case "int4": ret = OscarDbType.Integer; break; + case "int8": ret = OscarDbType.BigInt; break; + case "numeric": ret = OscarDbType.Numeric; break; + case "float4": ret = OscarDbType.Real; break; + case "float8": ret = OscarDbType.Double; break; + + case "bpchar": ret = OscarDbType.Char; break; + case "varchar": ret = OscarDbType.VarChar; break; + case "text": ret = OscarDbType.Text; break; + + case "timestamp": ret = OscarDbType.TimeStamp; break; + case "timestamptz": ret = OscarDbType.TimestampTZ; break; + case "date": ret = OscarDbType.Date; break; + case "time": ret = OscarDbType.Time; break; + case "timetz": ret = OscarDbType.TimeTZ; break; + case "interval": ret = OscarDbType.Interval; break; + + case "bool": ret = OscarDbType.Boolean; break; + case "bytea": ret = OscarDbType.Bytea; break; + case "bit": ret = OscarDbType.Bit; break; + + case "uuid": ret = OscarDbType.Char; break; + } + return isarray ? (ret | OscarDbType.Array) : ret; + } + + static readonly Dictionary _dicDbToCs = new Dictionary() { + { (int)OscarDbType.SmallInt, new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") }, + { (int)OscarDbType.Integer, new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") }, + { (int)OscarDbType.BigInt, new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") }, + { (int)OscarDbType.Numeric, new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") }, + { (int)OscarDbType.Real, new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") }, + { (int)OscarDbType.Double, new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") }, + + { (int)OscarDbType.Char, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + { (int)OscarDbType.VarChar, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + { (int)OscarDbType.Text, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + + { (int)OscarDbType.TimeStamp, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, + { (int)OscarDbType.TimestampTZ, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, + { (int)OscarDbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, + { (int)OscarDbType.Time, new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, + { (int)OscarDbType.TimeTZ, new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, + { (int)OscarDbType.Interval, new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, + + { (int)OscarDbType.Boolean, new DbToCs("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") }, + { (int)OscarDbType.Bytea, new DbToCs("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") }, + + /*** array ***/ + + { (int)(OscarDbType.SmallInt | OscarDbType.Array), new DbToCs("(short[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "short[]", typeof(short[]), typeof(short[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Integer | OscarDbType.Array), new DbToCs("(int[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "int[]", typeof(int[]), typeof(int[]), "{0}", "GetValue") }, + { (int)(OscarDbType.BigInt | OscarDbType.Array), new DbToCs("(long[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "long[]", typeof(long[]), typeof(long[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Numeric | OscarDbType.Array), new DbToCs("(decimal[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Real | OscarDbType.Array), new DbToCs("(float[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "float[]", typeof(float[]), typeof(float[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Double | OscarDbType.Array), new DbToCs("(double[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "double[]", typeof(double[]), typeof(double[]), "{0}", "GetValue") }, + + { (int)(OscarDbType.Char | OscarDbType.Array), new DbToCs("(string[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") }, + { (int)(OscarDbType.VarChar | OscarDbType.Array), new DbToCs("(string[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Text | OscarDbType.Array), new DbToCs("(string[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") }, + + { (int)(OscarDbType.TimeStamp | OscarDbType.Array), new DbToCs("(DateTime[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") }, + { (int)(OscarDbType.TimestampTZ | OscarDbType.Array), new DbToCs("(DateTime[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Date | OscarDbType.Array), new DbToCs("(DateTime[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Time | OscarDbType.Array), new DbToCs("(TimeSpan[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") }, + { (int)(OscarDbType.TimeTZ | OscarDbType.Array), new DbToCs("(TimeSpan[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Interval | OscarDbType.Array), new DbToCs("(TimeSpan[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") }, + + { (int)(OscarDbType.Boolean | OscarDbType.Array), new DbToCs("(bool[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "bool[]", typeof(bool[]), typeof(bool[]), "{0}", "GetValue") }, + { (int)(OscarDbType.Bytea | OscarDbType.Array), new DbToCs("(byte[][])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "byte[][]", typeof(byte[][]), typeof(byte[][]), "{0}", "GetValue") }, + }; + + public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null; + public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null; + public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null; + public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null; + public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null; + public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null; + public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null; + + public List GetDatabases() + { + var sql = @" select datname from sys_database"; + var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList(); + } + + public List GetTablesByDatabase(params string[] database) + { + var olddatabase = ""; + using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5))) + { + olddatabase = conn.Value.Database; + } + var dbs = database == null || database.Any() == false ? new[] { olddatabase } : database; + var tables = new List(); + + foreach (var db in dbs) + { + if (string.IsNullOrEmpty(db) || string.Compare(db, olddatabase, true) != 0) continue; + + var loc1 = new List(); + var loc2 = new Dictionary(); + var loc3 = new Dictionary>(); + + var sql = $@" +select +b.nspname || '.' || a.tablename, +a.schemaname, +a.tablename , +d.description, +'TABLE' +from sys_tables a +inner join sys_namespace b on b.nspname = a.schemaname +inner join sys_class c on c.relnamespace = b.oid and c.relname = a.tablename +left join sys_description d on d.objoid = c.oid and objsubid = 0 +where a.schemaname not in ('DIRECTORIES', 'INFO_SCHEM', 'REPLICATION', 'STAGENT', 'SYSAUDIT', 'SYSDBA', 'SYSFTSDBA', 'SYSSECURE', 'SYS_GLOBAL_TEMP', 'WMSYS') +and b.nspname || '.' || a.tablename not in ('PUBLIC.SPATIAL_REF_SYS') + +union all + +select +b.nspname || '.' || a.relname, +b.nspname, +a.relname, +d.description, +'VIEW' +from sys_class a +inner join sys_namespace b on b.oid = a.relnamespace +left join sys_description d on d.objoid = a.oid and objsubid = 0 +where b.nspname not in ('DIRECTORIES', 'INFO_SCHEM', 'REPLICATION', 'STAGENT', 'SYSAUDIT', 'SYSDBA', 'SYSFTSDBA', 'SYSSECURE', 'SYS_GLOBAL_TEMP', 'WMSYS') and a.relkind in ('m','v') +and b.nspname || '.' || a.relname not in ('PUBLIC.GEOGRAPHY_COLUMNS','PUBLIC.GEOMETRY_COLUMNS','PUBLIC.RASTER_COLUMNS','PUBLIC.RASTER_OVERVIEWS') +"; + var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var loc6 = new List(); + var loc66 = new List(); + var loc6_1000 = new List(); + var loc66_1000 = new List(); + foreach (object[] row in ds) + { + var object_id = string.Concat(row[0]); + var owner = string.Concat(row[1]); + var table = string.Concat(row[2]); + var comment = string.Concat(row[3]); + Enum.TryParse(string.Concat(row[4]), out var type); + loc2.Add(object_id, new DbTableInfo { Id = object_id.ToString(), Schema = owner, Name = table, Comment = comment, Type = type }); + loc3.Add(object_id, new Dictionary()); + switch (type) + { + case DbTableType.VIEW: + case DbTableType.TABLE: + loc6_1000.Add(object_id); + if (loc6_1000.Count >= 500) + { + loc6.Add(loc6_1000.ToArray()); + loc6_1000.Clear(); + } + break; + case DbTableType.StoreProcedure: + loc66_1000.Add(object_id); + if (loc66_1000.Count >= 500) + { + loc66.Add(loc66_1000.ToArray()); + loc66_1000.Clear(); + } + break; + } + } + if (loc6_1000.Count > 0) loc6.Add(loc6_1000.ToArray()); + if (loc66_1000.Count > 0) loc66.Add(loc66_1000.ToArray()); + + if (loc6.Count == 0) return loc1; + var loc8 = new StringBuilder().Append("("); + for (var loc8idx = 0; loc8idx < loc6.Count; loc8idx++) + { + if (loc8idx > 0) loc8.Append(" OR "); + loc8.Append("a.table_name in ("); + for (var loc8idx2 = 0; loc8idx2 < loc6[loc8idx].Length; loc8idx2++) + { + if (loc8idx2 > 0) loc8.Append(","); + loc8.Append($"'{loc6[loc8idx][loc8idx2]}'"); + } + loc8.Append(")"); + } + loc8.Append(")"); + + sql = $@" +select +ns.nspname || '.' || c.relname as id, +a.attname, +t.typname, +case when a.atttypmod > 0 and a.atttypmod < 32767 then a.atttypmod - 4 else a.attlen end len, +case when t.typelem = 0 then t.typname else t2.typname end, +case when a.attnotnull then 0 else 1 end as is_nullable, +--e.adsrc as is_identity, pg12以下 +(select sys_get_expr(adbin, adrelid) from sys_attrdef where adrelid = e.adrelid limit 1) is_identity, +d.description as comment, +a.attndims, +case when t.typelem = 0 then t.typtype else t2.typtype end, +ns2.nspname, +a.attnum +from sys_class c +inner join sys_attribute a on a.attnum > 0 and a.attrelid = c.oid +inner join sys_type t on t.oid = a.atttypid +left join sys_type t2 on t2.oid = t.typelem +left join sys_description d on d.objoid = a.attrelid and d.objsubid = a.attnum +left join sys_attrdef e on e.adrelid = a.attrelid and e.adnum = a.attnum +inner join sys_namespace ns on ns.oid = c.relnamespace +inner join sys_namespace ns2 on ns2.oid = t.typnamespace +where {loc8.ToString().Replace("a.table_name", "ns.nspname || '.' || c.relname")}"; + ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var position = 0; + foreach (object[] row in ds) + { + var object_id = string.Concat(row[0]); + var column = string.Concat(row[1]); + var type = string.Concat(row[2]); + var max_length = int.Parse(string.Concat(row[3])); + var sqlType = string.Concat(row[4]); + var is_nullable = string.Concat(row[5]) == "1"; + var is_identity = string.Concat(row[6]).StartsWith(@"NEXTVAL('") && string.Concat(row[6]).EndsWith(@"'::REGCLASS)"); + var comment = string.Concat(row[7]); + var defaultValue = string.Concat(row[6]); + int attndims = int.Parse(string.Concat(row[8])); + string typtype = string.Concat(row[9]); + string owner = string.Concat(row[10]); + int attnum = int.Parse(string.Concat(row[11])); + switch (sqlType.ToLower()) + { + case "bool": case "name": case "bit": case "varbit": case "bpchar": case "varchar": case "bytea": case "text": case "uuid": break; + default: max_length *= 8; break; + } + if (max_length <= 0) max_length = -1; + if (type.StartsWith("_")) + { + type = type.Substring(1); + if (attndims == 0) attndims++; + } + if (sqlType.StartsWith("_")) sqlType = sqlType.Substring(1); + if (max_length > 0) + { + switch (sqlType.ToLower()) + { + //case "numeric": sqlType += $"({max_length})"; break; + case "bpchar": case "varchar": case "bytea": case "bit": case "varbit": sqlType += $"({max_length})"; break; + } + } + + loc3[object_id].Add(column, new DbColumnInfo + { + Name = column, + MaxLength = max_length, + IsIdentity = is_identity, + IsNullable = is_nullable, + IsPrimary = false, + DbTypeText = type, + DbTypeTextFull = sqlType, + Table = loc2[object_id], + Coment = comment, + DefaultValue = defaultValue, + Position = ++position + }); + loc3[object_id][column].DbType = this.GetDbType(loc3[object_id][column]); + loc3[object_id][column].CsType = this.GetCsTypeInfo(loc3[object_id][column]); + } + + sql = $@" +select +ns.nspname || '.' || d.relname as table_id, +c.attname, +b.relname as index_id, +case when a.indisunique then 1 else 0 end IsUnique, +case when a.indisprimary then 1 else 0 end IsPrimary, +case when a.indisclustered then 0 else 1 end IsClustered, +case when sys_index_column_has_property(b.oid, c.attnum, 'desc') = 't' then 1 else 0 end IsDesc, +a.indkey::text, +c.attnum +from sys_index a +inner join sys_class b on b.oid = a.indexrelid +inner join sys_attribute c on c.attnum > 0 and c.attrelid = b.oid +inner join sys_namespace ns on ns.oid = b.relnamespace +inner join sys_class d on d.oid = a.indrelid +where {loc8.ToString().Replace("a.table_name", "ns.nspname || '.' || d.relname")} +"; + ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var indexColumns = new Dictionary>(); + var uniqueColumns = new Dictionary>(); + foreach (object[] row in ds) + { + var object_id = string.Concat(row[0]); + var column = string.Concat(row[1]); + var index_id = string.Concat(row[2]); + var is_unique = string.Concat(row[3]) == "1"; + var is_primary_key = string.Concat(row[4]) == "1"; + var is_clustered = string.Concat(row[5]) == "1"; + var is_desc = string.Concat(row[6]) == "1"; + var inkey = string.Concat(row[7]).Split(' '); + var attnum = int.Parse(string.Concat(row[8])); + attnum = int.Parse(inkey[attnum - 1]); + foreach (string tc in loc3[object_id].Keys) + { + if (loc3[object_id][tc].DbTypeText.EndsWith("[]")) + { + column = tc; + break; + } + } + if (loc3.ContainsKey(object_id) == false || loc3[object_id].ContainsKey(column) == false) continue; + var loc9 = loc3[object_id][column]; + if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key; + + Dictionary loc10 = null; + DbIndexInfo loc11 = null; + if (!indexColumns.TryGetValue(object_id, out loc10)) + indexColumns.Add(object_id, loc10 = new Dictionary()); + if (!loc10.TryGetValue(index_id, out loc11)) + loc10.Add(index_id, loc11 = new DbIndexInfo()); + loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc }); + if (is_unique && !is_primary_key) + { + if (!uniqueColumns.TryGetValue(object_id, out loc10)) + uniqueColumns.Add(object_id, loc10 = new Dictionary()); + if (!loc10.TryGetValue(index_id, out loc11)) + loc10.Add(index_id, loc11 = new DbIndexInfo()); + loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc }); + } + } + foreach (var object_id in indexColumns.Keys) + { + foreach (var column in indexColumns[object_id]) + loc2[object_id].IndexesDict.Add(column.Key, column.Value); + } + foreach (var object_id in uniqueColumns.Keys) + { + foreach (var column in uniqueColumns[object_id]) + { + column.Value.Columns.Sort((c1, c2) => c1.Column.Name.CompareTo(c2.Column.Name)); + loc2[object_id].UniquesDict.Add(column.Key, column.Value); + } + } + + sql = $@" +select +ns.nspname || '.' || b.relname as table_id, +array(select attname from sys_attribute where attrelid = a.conrelid and attnum = any(a.conkey)) as column_name, +a.conname as FKId, +ns2.nspname || '.' || c.relname as ref_table_id, +1 as IsForeignKey, +array(select attname from sys_attribute where attrelid = a.confrelid and attnum = any(a.confkey)) as ref_column, +null ref_sln, +null ref_table +from sys_constraint a +inner join sys_class b on b.oid = a.conrelid +inner join sys_class c on c.oid = a.confrelid +inner join sys_namespace ns on ns.oid = b.relnamespace +inner join sys_namespace ns2 on ns2.oid = c.relnamespace +where {loc8.ToString().Replace("a.table_name", "ns.nspname || '.' || b.relname")} +"; + ds = _orm.Ado.ExecuteArray(CommandType.Text, sql); + if (ds == null) return loc1; + + var fkColumns = new Dictionary>(); + foreach (object[] row in ds) + { + var table_id = string.Concat(row[0]); + var column = row[1] as string[]; + var fk_id = string.Concat(row[2]); + var ref_table_id = string.Concat(row[3]); + var is_foreign_key = string.Concat(row[4]) == "1"; + var referenced_column = row[5] as string[]; + var referenced_db = string.Concat(row[6]); + var referenced_table = string.Concat(row[7]); + + if (loc2.ContainsKey(ref_table_id) == false) continue; + + Dictionary loc12 = null; + DbForeignInfo loc13 = null; + if (!fkColumns.TryGetValue(table_id, out loc12)) + fkColumns.Add(table_id, loc12 = new Dictionary()); + if (!loc12.TryGetValue(fk_id, out loc13)) + loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[table_id], ReferencedTable = loc2[ref_table_id] }); + + for (int a = 0; a < column.Length; a++) + { + loc13.Columns.Add(loc3[table_id][column[a]]); + loc13.ReferencedColumns.Add(loc3[ref_table_id][referenced_column[a]]); + } + } + foreach (var table_id in fkColumns.Keys) + foreach (var fk in fkColumns[table_id]) + loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value); + + foreach (var table_id in loc3.Keys) + { + foreach (var loc5 in loc3[table_id].Values) + { + loc2[table_id].Columns.Add(loc5); + if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5); + if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5); + } + } + foreach (var loc4 in loc2.Values) + { + //if (loc4.Primarys.Count == 0 && loc4.UniquesDict.Count > 0) + //{ + // foreach (var loc5 in loc4.UniquesDict.First().Value.Columns) + // { + // loc5.Column.IsPrimary = true; + // loc4.Primarys.Add(loc5.Column); + // } + //} + loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name)); + loc4.Columns.Sort((c1, c2) => + { + int compare = c2.IsPrimary.CompareTo(c1.IsPrimary); + if (compare == 0) + { + bool b1 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any(); + bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any(); + compare = b2.CompareTo(b1); + } + if (compare == 0) compare = c1.Name.CompareTo(c2.Name); + return compare; + }); + loc1.Add(loc4); + } + loc1.Sort((t1, t2) => + { + var ret = t1.Schema.CompareTo(t2.Schema); + if (ret == 0) ret = t1.Name.CompareTo(t2.Name); + return ret; + }); + + loc2.Clear(); + loc3.Clear(); + tables.AddRange(loc1); + } + return tables; + } + + public class GetEnumsByDatabaseQueryInfo + { + public string name { get; set; } + public string label { get; set; } + } + public List GetEnumsByDatabase(params string[] database) + { + if (database == null || database.Length == 0) return new List(); + var drs = _orm.Ado.Query(CommandType.Text, _commonUtils.FormatSql(@" +select +ns.nspname || '.' || a.typname AS name, +b.enumlabel AS label +from sys_type a +inner join sys_enum b on b.enumtypid = a.oid +inner join sys_namespace ns on ns.oid = a.typnamespace +where a.typtype = 'e' and ns.nspname in (SELECT schema_name FROM information_schema.schemata where catalog_name in {0})", database)); + var ret = new Dictionary>(); + foreach (var dr in drs) + { + if (ret.TryGetValue(dr.name, out var labels) == false) ret.Add(dr.name, labels = new Dictionary()); + var key = dr.label; + if (Regex.IsMatch(key, @"^[\u0391-\uFFE5a-zA-Z_\$][\u0391-\uFFE5a-zA-Z_\$\d]*$") == false) + key = $"Unkown{ret[dr.name].Count + 1}"; + if (labels.ContainsKey(key) == false) labels.Add(key, dr.label); + } + return ret.Select(a => new DbEnumInfo { Name = a.Key, Labels = a.Value }).ToList(); + } + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongExpression.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongExpression.cs new file mode 100644 index 00000000..35e04a67 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongExpression.cs @@ -0,0 +1,571 @@ +using FreeSql.Internal; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Text.RegularExpressions; + +namespace FreeSql.ShenTong +{ + class ShenTongExpression : CommonExpression + { + + public ShenTongExpression(CommonUtils common) : base(common) { } + + public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + switch (exp.NodeType) + { + case ExpressionType.Convert: + var operandExp = (exp as UnaryExpression)?.Operand; + var gentype = exp.Type.NullableTypeOrThis(); + if (gentype != operandExp.Type.NullableTypeOrThis()) + { + switch (exp.Type.NullableTypeOrThis().ToString()) + { + case "System.Boolean": return $"(({getExp(operandExp)})::text not in ('0','false','f','no'))"; + case "System.Byte": return $"({getExp(operandExp)})::int2"; + case "System.Char": return $"substr(({getExp(operandExp)})::char, 1, 1)"; + case "System.DateTime": return $"({getExp(operandExp)})::timestamp"; + case "System.Decimal": return $"({getExp(operandExp)})::numeric"; + case "System.Double": return $"({getExp(operandExp)})::float8"; + case "System.Int16": return $"({getExp(operandExp)})::int2"; + case "System.Int32": return $"({getExp(operandExp)})::int4"; + case "System.Int64": return $"({getExp(operandExp)})::int8"; + case "System.SByte": return $"({getExp(operandExp)})::int2"; + case "System.Single": return $"({getExp(operandExp)})::float4"; + case "System.String": return $"({getExp(operandExp)})::text"; + case "System.UInt16": return $"({getExp(operandExp)})::int2"; + case "System.UInt32": return $"({getExp(operandExp)})::int4"; + case "System.UInt64": return $"({getExp(operandExp)})::int8"; + case "System.Guid": return $"({getExp(operandExp)})::bpchar(36)"; + } + } + break; + case ExpressionType.ArrayLength: + var arrOperExp = getExp((exp as UnaryExpression).Operand); + if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)"; + return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end"; + case ExpressionType.Call: + var callExp = exp as MethodCallExpression; + + switch (callExp.Method.Name) + { + case "Parse": + case "TryParse": + switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) + { + case "System.Boolean": return $"(({getExp(callExp.Arguments[0])})::text not in ('0','false','f','no'))"; + case "System.Byte": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.Char": return $"substr(({getExp(callExp.Arguments[0])})::char, 1, 1)"; + case "System.DateTime": return $"({getExp(callExp.Arguments[0])})::timestamp"; + case "System.Decimal": return $"({getExp(callExp.Arguments[0])})::numeric"; + case "System.Double": return $"({getExp(callExp.Arguments[0])})::float8"; + case "System.Int16": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.Int32": return $"({getExp(callExp.Arguments[0])})::int4"; + case "System.Int64": return $"({getExp(callExp.Arguments[0])})::int8"; + case "System.SByte": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.Single": return $"({getExp(callExp.Arguments[0])})::float4"; + case "System.UInt16": return $"({getExp(callExp.Arguments[0])})::int2"; + case "System.UInt32": return $"({getExp(callExp.Arguments[0])})::int4"; + case "System.UInt64": return $"({getExp(callExp.Arguments[0])})::int8"; + case "System.Guid": return $"({getExp(callExp.Arguments[0])})::bpchar(36)"; + } + break; + case "NewGuid": + return null; + case "Next": + if (callExp.Object?.Type == typeof(Random)) return "(random()*1000000000)::int8"; + return null; + case "NextDouble": + if (callExp.Object?.Type == typeof(Random)) return "random()"; + return null; + case "Random": + if (callExp.Method.DeclaringType.IsNumberType()) return "random()"; + return null; + case "ToString": + if (callExp.Object != null) return callExp.Arguments.Count == 0 ? $"({getExp(callExp.Object)})::text" : null; + return null; + } + + var objExp = callExp.Object; + var objType = objExp?.Type; + if (objType?.FullName == "System.Byte[]") return null; + + var argIndex = 0; + if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) + { + objExp = callExp.Arguments.FirstOrDefault(); + objType = objExp?.Type; + argIndex++; + } + if (objType == null) objType = callExp.Method.DeclaringType; + if (objType != null || objType.IsArrayOrList()) + { + string left = null; + switch (callExp.Method.Name) + { + case "Any": + left = objExp == null ? null : getExp(objExp); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + return $"(case when {left} is null then 0 else array_length({left},1) end > 0)"; + case "Contains": + tsc.SetMapColumnTmp(null); + var args1 = getExp(callExp.Arguments[argIndex]); + var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp); + var oldDbParams = tsc.SetDbParamsReturnOld(null); + left = objExp == null ? null : getExp(objExp); + tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType); + tsc.SetDbParamsReturnOld(oldDbParams); + //判断 in 或 array @> array + if (left.StartsWith("array[") || left.EndsWith("]")) + return $"({args1}) in ({left.Substring(6, left.Length - 7)})"; + if (left.StartsWith("(") || left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格 + return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})"; + if (args1.StartsWith("(") || args1.EndsWith(")")) args1 = $"array[{args1.TrimStart('(').TrimEnd(')')}]"; + args1 = $"array[{args1}]"; + if (objExp != null) + { + var dbinfo = _common._orm.CodeFirst.GetDbInfo(objExp.Type); + if (dbinfo != null) args1 = $"{args1}::{dbinfo.dbtype}"; + } + return $"({left} @> {args1})"; + case "Concat": + left = objExp == null ? null : getExp(objExp); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + var right2 = getExp(callExp.Arguments[argIndex]); + if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]"; + return $"({left} || {right2})"; + case "GetLength": + case "GetLongLength": + case "Length": + case "Count": + left = objExp == null ? null : getExp(objExp); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + return $"case when {left} is null then 0 else array_length({left},1) end"; + } + } + break; + case ExpressionType.MemberAccess: + var memExp = exp as MemberExpression; + var memParentExp = memExp.Expression?.Type; + if (memParentExp?.FullName == "System.Byte[]") return null; + if (memParentExp != null) + { + if (memParentExp.IsArray == true) + { + var left = getExp(memExp.Expression); + if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]"; + switch (memExp.Member.Name) + { + case "Length": + case "Count": return $"case when {left} is null then 0 else array_length({left},1) end"; + } + } + } + break; + case ExpressionType.NewArrayInit: + var arrExp = exp as NewArrayExpression; + var arrSb = new StringBuilder(); + arrSb.Append("array["); + for (var a = 0; a < arrExp.Expressions.Count; a++) + { + if (a > 0) arrSb.Append(","); + arrSb.Append(getExp(arrExp.Expressions[a])); + } + if (arrSb.Length == 1) arrSb.Append("NULL"); + return arrSb.Append("]").ToString(); + case ExpressionType.ListInit: + var listExp = exp as ListInitExpression; + var listSb = new StringBuilder(); + listSb.Append("("); + for (var a = 0; a < listExp.Initializers.Count; a++) + { + if (listExp.Initializers[a].Arguments.Any() == false) continue; + if (a > 0) listSb.Append(","); + listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault())); + } + if (listSb.Length == 1) listSb.Append("NULL"); + return listSb.Append(")").ToString(); + case ExpressionType.New: + var newExp = exp as NewExpression; + if (typeof(IList).IsAssignableFrom(newExp.Type)) + { + if (newExp.Arguments.Count == 0) return "(NULL)"; + if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)"; + return getExp(newExp.Arguments[0]); + } + return null; + } + return null; + } + + public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Empty": return "''"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Length": return $"char_length({left})"; + } + return null; + } + public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Now": return _common.Now; + case "UtcNow": return _common.NowUtc; + case "Today": return "date_trunc('D',current_date)"; + case "MinValue": return "'0001/1/1 0:00:00'::timestamp"; + case "MaxValue": return "'9999/12/31 23:59:59'::timestamp"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Date": return $"date_trunc('D',{left})"; + case "TimeOfDay": return $"datediff('second',date_trunc('D',{left}),{left})"; + case "DayOfWeek": return $"(dayofweek({left})-1)"; + case "Day": return $"dayofmonth({left})"; + case "DayOfYear": return $"dayofyear({left})"; + case "Month": return $"month({left})"; + case "Year": return $"year({left})"; + case "Hour": return $"hour({left})"; + case "Minute": return $"minute({left})"; + case "Second": return $"second({left})"; + case "Millisecond": return $"(datepart(millisecond,{left})-datepart(second,{left})*1000)"; + case "Ticks": return $"(datediff('second','1970-1-1',{left})::int8*10000000+621355968000000000)"; + } + return null; + } + public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Zero": return "0"; + case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10 + case "MaxValue": return "922337203685477580"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Days": return $"floor(({left})/{60 * 60 * 24})"; + case "Hours": return $"floor(({left})/{60 * 60}%24)"; + case "Milliseconds": return $"(({left})::int8*1000)"; + case "Minutes": return $"floor(({left})/60%60)"; + case "Seconds": return $"(({left})%60)"; + case "Ticks": return $"(({left})::int8*10000000)"; + case "TotalDays": return $"(({left})/{60 * 60 * 24})"; + case "TotalHours": return $"(({left})/{60 * 60})"; + case "TotalMilliseconds": return $"(({left})::int8*1000)"; + case "TotalMinutes": return $"(({left})/60)"; + case "TotalSeconds": return $"({left})"; + } + return null; + } + + public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "IsNullOrEmpty": + var arg1 = getExp(exp.Arguments[0]); + return $"({arg1} is null or {arg1} = '')"; + case "IsNullOrWhiteSpace": + var arg2 = getExp(exp.Arguments[0]); + return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')"; + case "Concat": + return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null); + } + } + else + { + var left = getExp(exp.Object); + switch (exp.Method.Name) + { + case "StartsWith": + case "EndsWith": + case "Contains": + var args0Value = getExp(exp.Arguments[0]); + if (args0Value == "NULL") return $"({left}) IS NULL"; + var likeOpt = "LIKE"; + if (exp.Arguments.Count > 1) + { + if (exp.Arguments[1].Type == typeof(bool) || + exp.Arguments[1].Type == typeof(StringComparison) && getExp(exp.Arguments[0]).Contains("IgnoreCase")) likeOpt = "ILIKE"; + } + if (exp.Method.Name == "StartsWith") return $"({left}) {likeOpt} {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"(({args0Value})::text || '%')")}"; + if (exp.Method.Name == "EndsWith") return $"({left}) {likeOpt} {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"('%' || ({args0Value})::text)")}"; + if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) {likeOpt} {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}"; + return $"({left}) {likeOpt} ('%' || ({args0Value})::text || '%')"; + case "ToLower": return $"lower({left})"; + case "ToUpper": return $"upper({left})"; + case "Substring": + var substrArgs1 = getExp(exp.Arguments[0]); + if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString(); + else substrArgs1 += "+1"; + if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})"; + return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})"; + case "IndexOf": return $"(strpos({left}, {getExp(exp.Arguments[0])})-1)"; + case "PadLeft": + if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})"; + return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "PadRight": + if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})"; + return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Trim": + case "TrimStart": + case "TrimEnd": + if (exp.Arguments.Count == 0) + { + if (exp.Method.Name == "Trim") return $"trim({left})"; + if (exp.Method.Name == "TrimStart") return $"ltrim({left})"; + if (exp.Method.Name == "TrimEnd") return $"rtrim({left})"; + } + var trimArg1 = ""; + var trimArg2 = ""; + foreach (var argsTrim02 in exp.Arguments) + { + var argsTrim01s = new[] { argsTrim02 }; + if (argsTrim02.NodeType == ExpressionType.NewArrayInit) + { + var arritem = argsTrim02 as NewArrayExpression; + argsTrim01s = arritem.Expressions.ToArray(); + } + foreach (var argsTrim01 in argsTrim01s) + { + var trimChr = getExp(argsTrim01).Trim('\''); + if (trimChr.Length == 1) trimArg1 += trimChr; + else trimArg2 += $" || ({trimChr})"; + } + } + if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + return left; + case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end"; + case "Equals": return $"({left} = ({getExp(exp.Arguments[0])})::text)"; + } + } + return null; + } + public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + switch (exp.Method.Name) + { + case "Abs": return $"abs({getExp(exp.Arguments[0])})"; + case "Sign": return $"sign({getExp(exp.Arguments[0])})"; + case "Floor": return $"floor({getExp(exp.Arguments[0])})"; + case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})"; + case "Round": + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + return $"round({getExp(exp.Arguments[0])})"; + case "Exp": return $"exp({getExp(exp.Arguments[0])})"; + case "Log": return $"log({getExp(exp.Arguments[0])})"; + case "Log10": return $"log10({getExp(exp.Arguments[0])})"; + case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})"; + case "Cos": return $"cos({getExp(exp.Arguments[0])})"; + case "Sin": return $"sin({getExp(exp.Arguments[0])})"; + case "Tan": return $"tan({getExp(exp.Arguments[0])})"; + case "Acos": return $"acos({getExp(exp.Arguments[0])})"; + case "Asin": return $"asin({getExp(exp.Arguments[0])})"; + case "Atan": return $"atan({getExp(exp.Arguments[0])})"; + case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; + case "Truncate": return $"trunc({getExp(exp.Arguments[0])}, 0)"; + } + return null; + } + public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))"; + case "DaysInMonth": return $"dayofmonth(dateadd('day',-1,dateadd('month',1,({getExp(exp.Arguments[0])})::text||'-'||({getExp(exp.Arguments[1])})::text||'-1')))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + + case "IsLeapYear": + var isLeapYearArgs1 = getExp(exp.Arguments[0]); + return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)"; + + case "Parse": return $"({getExp(exp.Arguments[0])})::timestamp"; + case "ParseExact": + case "TryParse": + case "TryParseExact": return $"({getExp(exp.Arguments[0])})::timestamp"; + } + } + else + { + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); + switch (exp.Method.Name) + { + case "Add": return $"dateadd('second',{args1},{left})"; + case "AddDays": return $"dateadd('day',{args1},{left})"; + case "AddHours": return $"dateadd('hour',{args1},{left})"; + case "AddMilliseconds": return $"dateadd('second',({args1})/1000,{left})"; + case "AddMinutes": return $"dateadd('minute',{args1},{left})"; + case "AddMonths": return $"dateadd('month',{args1},{left})"; + case "AddSeconds": return $"dateadd('second', {args1}, {left})"; + case "AddTicks": return $"dateadd('second',({args1})/10000000,{left})"; + case "AddYears": return $"dateadd('year',{args1},{left})"; + case "Subtract": + switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName) + { + case "System.DateTime": return $"datediff('second',{args1},{left})"; + case "System.TimeSpan": return $"dateadd('second',({args1})*-1,{left})"; + } + break; + case "Equals": return $"({left} = {args1})"; + case "CompareTo": return $"datediff('second',{args1},{left})"; + case "ToString": + if (left.EndsWith("::timestamp") == false) left = $"({left})::timestamp"; + if (exp.Arguments.Count == 0) return $"to_char({left},'YYYY-MM-DD HH24:MI:SS.US')"; + switch (args1) + { + case "'yyyy-MM-dd HH:mm:ss'": return $"to_char({left},'YYYY-MM-DD HH24:MI:SS')"; + case "'yyyy-MM-dd HH:mm'": return $"to_char({left},'YYYY-MM-DD HH24:MI')"; + case "'yyyy-MM-dd HH'": return $"to_char({left},'YYYY-MM-DD HH24')"; + case "'yyyy-MM-dd'": return $"to_char({left},'YYYY-MM-DD')"; + case "'yyyy-MM'": return $"to_char({left},'YYYY-MM')"; + case "'yyyyMMddHHmmss'": return $"to_char({left},'YYYYMMDDHH24MISS')"; + case "'yyyyMMddHHmm'": return $"to_char({left},'YYYYMMDDHH24MI')"; + case "'yyyyMMddHH'": return $"to_char({left},'YYYYMMDDHH24')"; + case "'yyyyMMdd'": return $"to_char({left},'YYYYMMDD')"; + case "'yyyyMM'": return $"to_char({left},'YYYYMM')"; + case "'yyyy'": return $"to_char({left},'YYYY')"; + case "'HH:mm:ss'": return $"to_char({left},'HH24:MI:SS')"; + } + args1 = Regex.Replace(args1, "(yyyy|yy|MM|dd|HH|hh|mm|ss|tt)", m => + { + switch (m.Groups[1].Value) + { + case "yyyy": return $"YYYY"; + case "yy": return $"YY"; + case "MM": return $"%_a1"; + case "dd": return $"%_a2"; + case "HH": return $"%_a3"; + case "hh": return $"%_a4"; + case "mm": return $"%_a5"; + case "ss": return $"SS"; + case "tt": return $"%_a6"; + } + return m.Groups[0].Value; + }); + var argsFinds = new[] { "YYYY", "YY", "%_a1", "%_a2", "%_a3", "%_a4", "%_a5", "SS", "%_a6" }; + var argsSpts = Regex.Split(args1, "(M|d|H|h|m|s|t)"); + for (var a = 0; a < argsSpts.Length; a++) + { + switch (argsSpts[a]) + { + case "M": argsSpts[a] = $"ltrim(to_char({left},'MM'),'0')"; break; + case "d": argsSpts[a] = $"case when substr(to_char({left},'DD'),1,1) = '0' then substr(to_char({left},'DD'),2,1) else to_char({left},'DD') end"; break; + case "H": argsSpts[a] = $"case when substr(to_char({left},'HH24'),1,1) = '0' then substr(to_char({left},'HH24'),2,1) else to_char({left},'HH24') end"; break; + case "h": argsSpts[a] = $"case when substr(to_char({left},'HH12'),1,1) = '0' then substr(to_char({left},'HH12'),2,1) else to_char({left},'HH12') end"; break; + case "m": argsSpts[a] = $"case when substr(to_char({left},'MI'),1,1) = '0' then substr(to_char({left},'MI'),2,1) else to_char({left},'MI') end"; break; + case "s": argsSpts[a] = $"case when substr(to_char({left},'SS'),1,1) = '0' then substr(to_char({left},'SS'),2,1) else to_char({left},'SS') end"; break; + case "t": argsSpts[a] = $"rtrim(to_char({left},'AM'),'M')"; break; + default: + var argsSptsA = argsSpts[a]; + if (argsSptsA.StartsWith("'")) argsSptsA = argsSptsA.Substring(1); + if (argsSptsA.EndsWith("'")) argsSptsA = argsSptsA.Remove(argsSptsA.Length - 1); + argsSpts[a] = argsFinds.Any(m => argsSptsA.Contains(m)) ? $"to_char({left},'{argsSptsA}')" : $"'{argsSptsA}'"; + break; + } + } + if (argsSpts.Length > 0) args1 = $"({string.Join(" || ", argsSpts.Where(a => a != "''"))})"; + return args1.Replace("%_a1", "MM").Replace("%_a2", "DD").Replace("%_a3", "HH24").Replace("%_a4", "HH12").Replace("%_a5", "MI").Replace("%_a6", "AM"); + } + } + return null; + } + public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})"; + case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})"; + case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)"; + case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)"; + case "FromSeconds": return $"({getExp(exp.Arguments[0])})"; + case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)"; + case "Parse": return $"({getExp(exp.Arguments[0])})::int8"; + case "ParseExact": + case "TryParse": + case "TryParseExact": return $"({getExp(exp.Arguments[0])})::int8"; + } + } + else + { + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); + switch (exp.Method.Name) + { + case "Add": return $"({left}+{args1})"; + case "Subtract": return $"({left}-({args1}))"; + case "Equals": return $"({left} = {args1})"; + case "CompareTo": return $"({left}-({args1}))"; + case "ToString": return $"({left})::text"; + } + } + return null; + } + public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "ToBoolean": return $"(({getExp(exp.Arguments[0])})::text not in ('0','false','f','no'))"; + case "ToByte": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToChar": return $"substr(({getExp(exp.Arguments[0])})::char, 1, 1)"; + case "ToDateTime": return $"({getExp(exp.Arguments[0])})::timestamp"; + case "ToDecimal": return $"({getExp(exp.Arguments[0])})::numeric"; + case "ToDouble": return $"({getExp(exp.Arguments[0])})::float8"; + case "ToInt16": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToInt32": return $"({getExp(exp.Arguments[0])})::int4"; + case "ToInt64": return $"({getExp(exp.Arguments[0])})::int8"; + case "ToSByte": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToSingle": return $"({getExp(exp.Arguments[0])})::float4"; + case "ToString": return $"({getExp(exp.Arguments[0])})::text"; + case "ToUInt16": return $"({getExp(exp.Arguments[0])})::int2"; + case "ToUInt32": return $"({getExp(exp.Arguments[0])})::int4"; + case "ToUInt64": return $"({getExp(exp.Arguments[0])})::int8"; + } + } + return null; + } + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongExtensions.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongExtensions.cs new file mode 100644 index 00000000..69d2f475 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongExtensions.cs @@ -0,0 +1,17 @@ +using FreeSql; +using FreeSql.ShenTong.Curd; +using System; +using System.Linq.Expressions; + +public static partial class FreeSqlShenTongGlobalExtensions +{ + + /// + /// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换 + /// + /// + /// + /// + public static string FormatShenTong(this string that, params object[] args) => _shentongAdo.Addslashes(that, args); + static FreeSql.ShenTong.ShenTongAdo _shentongAdo = new FreeSql.ShenTong.ShenTongAdo(); +} diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongProvider.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongProvider.cs new file mode 100644 index 00000000..25f30f08 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongProvider.cs @@ -0,0 +1,64 @@ +using FreeSql.Internal; +using FreeSql.Internal.CommonProvider; +using FreeSql.ShenTong.Curd; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq.Expressions; +using System.Net; +using System.Net.NetworkInformation; +using System.Threading; + +namespace FreeSql.ShenTong +{ + + public class ShenTongProvider : IFreeSql + { + public ISelect Select() where T1 : class => new ShenTongSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, null); + public ISelect Select(object dywhere) where T1 : class => new ShenTongSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsert Insert() where T1 : class => new ShenTongInsert(this, this.InternalCommonUtils, this.InternalCommonExpression); + public IInsert Insert(T1 source) where T1 : class => this.Insert().AppendData(source); + public IInsert Insert(T1[] source) where T1 : class => this.Insert().AppendData(source); + public IInsert Insert(List source) where T1 : class => this.Insert().AppendData(source); + public IInsert Insert(IEnumerable source) where T1 : class => this.Insert().AppendData(source); + public IUpdate Update() where T1 : class => new ShenTongUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, null); + public IUpdate Update(object dywhere) where T1 : class => new ShenTongUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IDelete Delete() where T1 : class => new ShenTongDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, null); + public IDelete Delete(object dywhere) where T1 : class => new ShenTongDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public IInsertOrUpdate InsertOrUpdate() where T1 : class => new ShenTongInsertOrUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression); + + public IAdo Ado { get; } + public IAop Aop { get; } + public ICodeFirst CodeFirst { get; } + public IDbFirst DbFirst => throw new NotImplementedException("正在支持中"); + public ShenTongProvider(string masterConnectionString, string[] slaveConnectionString, Func connectionFactory = null) + { + this.InternalCommonUtils = new ShenTongUtils(this); + this.InternalCommonExpression = new ShenTongExpression(this.InternalCommonUtils); + + this.Ado = new ShenTongAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory); + this.Aop = new AopProvider(); + + //this.DbFirst = new ShenTongDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression); + this.CodeFirst = new ShenTongCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression); + } + + internal CommonUtils InternalCommonUtils { get; } + internal CommonExpression InternalCommonExpression { get; } + + public void Transaction(Action handler) => Ado.Transaction(handler); + public void Transaction(IsolationLevel isolationLevel, Action handler) => Ado.Transaction(isolationLevel, handler); + + public GlobalFilter GlobalFilter { get; } = new GlobalFilter(); + + ~ShenTongProvider() => this.Dispose(); + int _disposeCounter; + public void Dispose() + { + if (Interlocked.Increment(ref _disposeCounter) != 1) return; + (this.Ado as AdoProvider)?.Dispose(); + } + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs b/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs new file mode 100644 index 00000000..227d7c13 --- /dev/null +++ b/Providers/FreeSql.Provider.ShenTong/ShenTongUtils.cs @@ -0,0 +1,173 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data.Common; +using System.Data.OscarClient; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Text; + +namespace FreeSql.ShenTong +{ + + class ShenTongUtils : CommonUtils + { + public ShenTongUtils(IFreeSql orm) : base(orm) + { + } + + static Array getParamterArrayValue(Type arrayType, object value, object defaultValue) + { + var valueArr = value as Array; + var len = valueArr.GetLength(0); + var ret = Array.CreateInstance(arrayType, len); + for (var a = 0; a < len; a++) + { + var item = valueArr.GetValue(a); + ret.SetValue(item == null ? defaultValue : getParamterValue(item.GetType(), item, 1), a); + } + return ret; + } + static Dictionary> dicGetParamterValue = new Dictionary> { + { typeof(uint).FullName, a => long.Parse(string.Concat(a)) }, { typeof(uint[]).FullName, a => getParamterArrayValue(typeof(long), a, 0) }, { typeof(uint?[]).FullName, a => getParamterArrayValue(typeof(long?), a, null) }, + { typeof(ulong).FullName, a => decimal.Parse(string.Concat(a)) }, { typeof(ulong[]).FullName, a => getParamterArrayValue(typeof(decimal), a, 0) }, { typeof(ulong?[]).FullName, a => getParamterArrayValue(typeof(decimal?), a, null) }, + { typeof(ushort).FullName, a => int.Parse(string.Concat(a)) }, { typeof(ushort[]).FullName, a => getParamterArrayValue(typeof(int), a, 0) }, { typeof(ushort?[]).FullName, a => getParamterArrayValue(typeof(int?), a, null) }, + { typeof(byte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(byte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(byte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) }, + { typeof(sbyte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(sbyte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(sbyte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) }, + }; + static object getParamterValue(Type type, object value, int level = 0) + { + if (type.FullName == "System.Byte[]") return value; + if (type.IsArray && level == 0) + { + var elementType = type.GetElementType(); + Type enumType = null; + if (elementType.IsEnum) enumType = elementType; + else if (elementType.IsNullableType()) + { + var genericTypesFirst = elementType.GetGenericArguments().First(); + if (genericTypesFirst.IsEnum) enumType = genericTypesFirst; + } + if (enumType != null) return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ? + getParamterArrayValue(typeof(long), value, elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue()) : + getParamterArrayValue(typeof(int), value, elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue()); + return dicGetParamterValue.TryGetValue(type.FullName, out var trydicarr) ? trydicarr(value) : value; + } + if (type.IsNullableType()) type = type.GetGenericArguments().First(); + if (type.IsEnum) return (int)value; + if (dicGetParamterValue.TryGetValue(type.FullName, out var trydic)) return trydic(value); + return value; + } + + public override DbParameter AppendParamter(List _params, string parameterName, ColumnInfo col, Type type, object value) + { + if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}"; + if (value != null) value = getParamterValue(type, value); + var ret = new OscarParameter { ParameterName = QuoteParamterName(parameterName), Value = value }; + //if (value.GetType().IsEnum || value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true) { + // ret.DataTypeName = ""; + //} else { + var tp = _orm.CodeFirst.GetDbInfo(type)?.type; + if (tp != null) ret.OscarDbType = (OscarDbType)tp.Value; + if (col != null) + { + var dbtype = (OscarDbType)_orm.DbFirst.GetDbType(new DatabaseModel.DbColumnInfo { DbTypeText = col.DbTypeText }); + if (dbtype != OscarDbType.Oidvector) + { + ret.OscarDbType = dbtype; + if (col.DbSize != 0) ret.Size = col.DbSize; + if (col.DbPrecision != 0) ret.Precision = col.DbPrecision; + if (col.DbScale != 0) ret.Scale = col.DbScale; + } + } + //} + _params?.Add(ret); + return ret; + } + + public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => + Utils.GetDbParamtersByObject(sql, obj, "@", (name, type, value) => + { + if (value != null) value = getParamterValue(type, value); + var ret = new OscarParameter { ParameterName = $"@{name}", Value = value }; + //if (value.GetType().IsEnum || value.GetType().GenericTypeArguments.FirstOrDefault()?.IsEnum == true) { + // ret.DataTypeName = ""; + //} else { + var tp = _orm.CodeFirst.GetDbInfo(type)?.type; + if (tp != null) ret.OscarDbType = (OscarDbType)tp.Value; + //} + return ret; + }); + + public override string FormatSql(string sql, params object[] args) => sql?.FormatShenTong(args); + public override string QuoteSqlName(params string[] name) + { + if (name.Length == 1) + { + var nametrim = name[0].Trim(); + if (nametrim.StartsWith("(") && nametrim.EndsWith(")")) + return nametrim; //原生SQL + if (nametrim.StartsWith("\"") && nametrim.EndsWith("\"")) + return nametrim; + return $"\"{nametrim.Replace(".", "\".\"")}\""; + } + return $"\"{string.Join("\".\"", name)}\""; + } + public override string TrimQuoteSqlName(string name) + { + var nametrim = name.Trim(); + if (nametrim.StartsWith("(") && nametrim.EndsWith(")")) + return nametrim; //原生SQL + return $"{nametrim.Trim('"').Replace("\".\"", ".").Replace(".\"", ".")}"; + } + public override string[] SplitTableName(string name) => GetSplitTableNames(name, '"', '"', 2); + public override string QuoteParamterName(string name) => $"@{name}"; + public override string IsNull(string sql, object value) => $"coalesce({sql}, {value})"; + public override string StringConcat(string[] objs, Type[] types) => $"{string.Join(" || ", objs)}"; + public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}"; + public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}"; + public override string Now => "current_timestamp"; + public override string NowUtc => "(current_timestamp at time zone 'UTC')"; + + public override string QuoteWriteParamter(Type type, string paramterName) => paramterName; + public override string QuoteReadColumn(Type type, Type mapType, string columnName) => columnName; + + public override string GetNoneParamaterSqlValue(List specialParams, Type type, object value) + { + if (value == null) return "NULL"; + if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value); + value = getParamterValue(type, value); + var type2 = value.GetType(); + if (type2 == typeof(byte[])) return $"0x{CommonUtils.BytesSqlRaw(value as byte[])}"; + if (type2 == typeof(TimeSpan) || type2 == typeof(TimeSpan?)) + { + var ts = (TimeSpan)value; + var hh = Math.Min(24, (int)Math.Floor(ts.TotalHours)); + if (hh >= 24) hh = 0; + value = $"{hh}:{ts.Minutes}:{ts.Seconds}.{ts.Milliseconds}"; + } + else if (value is Array) + { + var valueArr = value as Array; + var eleType = type2.GetElementType(); + var len = valueArr.GetLength(0); + var sb = new StringBuilder().Append("ARRAY["); + for (var a = 0; a < len; a++) + { + var item = valueArr.GetValue(a); + if (a > 0) sb.Append(","); + sb.Append(GetNoneParamaterSqlValue(specialParams, eleType, item)); + } + sb.Append("]"); + var dbinfo = _orm.CodeFirst.GetDbInfo(type); + if (dbinfo != null) sb.Append("::").Append(dbinfo.dbtype); + return sb.ToString(); + } + return FormatSql("{0}", value, 1); + } + } +} diff --git a/Providers/FreeSql.Provider.ShenTong/key.snk b/Providers/FreeSql.Provider.ShenTong/key.snk new file mode 100644 index 0000000000000000000000000000000000000000..be1149357b2060cb16aa173fa54ed5712e14d886 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098e+`w>!pw~z{kC)(11aic*UA-FJmj*Dw%pGP3~em+rOVqNP@MPQ<0SE0Jw z18wO2D&7?q5r~)y-xHI!=Z`X~3NABfxO*Rt(RKB5xZ1m4DrJj|kVnrZK!68?r~F(^ zF8&@B#vuvJ#~ISY*N$A>FP8GV$Gww<4lAIG`fLSf8o=U$8Q2V1Dw!uRoO@@IXo{{& zGRd>cseR-YBR9`Y3Sh=ZL3z~q?`Grcb>lHDLxwW3}jwu`6-eT*nfH1(XM}Q}C z10NXPe~;ciLC3eO6W_Y0G5RRIAI`cz)T{=Wk%f7<+bDoBRp}Jl!>1+*GaI5un*m5A zkcb_mp6S*!@b5a)>R_yLok1277f`x;0vtz@?PY`FIKqT%3pJ(P8dH+sTWP4`7ovcs zP!O_$q&X`AkNRQULp(?74(v}<+FqM*HT3bo-qLW6n9X*AD6e%;#Z)6<@>~y2hlubU z`Y}D>OQ(AcS4{b1_FAgVBYrD+k!dUgJF0}jGb`A|sl|+^Ai@M&x=dhq`GiOJT&(@p zSnV_FdZQ_y4H|c{MA$s3-uJ>b`BkX%wA!qSd{u8a`l*9Q;gOChlC^WMd@Ts92&B^Q iBbsxrKxd~B8LVo7`w{*T8NJ6Jg9$1yt#;1QpFdUzz91$5 literal 0 HcmV?d00001 diff --git a/Providers/FreeSql.Provider.ShenTong/lib/Mono.Security.dll b/Providers/FreeSql.Provider.ShenTong/lib/Mono.Security.dll new file mode 100644 index 0000000000000000000000000000000000000000..6accde79112f9dec05ca6faf540f9f8f16cbe481 GIT binary patch literal 282624 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%0AntS9A%ww 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%0DRJ0X5BgE4dU*BxAX-~e7=Ax-aEg| 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
Zl4A2)gkl|ov#WxYyTbSy{QR0;^ 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%0A~*%!r&l;H*7_(z#ef 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

sEC$+=7I=VvHI~u$^=dZ#` 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%0D%!WrJpg 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

0yD&}im1*}!YV$5rREoE$V^f2=}V5fRu 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

H(WMul8a)}DlDHuS{b!qVSa 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 Yz$^x4F))jPSq#i#U={=apJU+v0sdcDM*si- literal 0 HcmV?d00001