Browse Source

finish sqlite and SqlServer

Lith 11 tháng trước cách đây
mục cha
commit
586bb8999e
73 tập tin đã thay đổi với 3232 bổ sung4382 xóa
  1. 4 10
      README.md
  2. 17 16
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/DataSource.cs
  3. 0 181
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_Group_Test.cs
  4. 0 218
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_InnerJoin_ByJoin_Test.cs
  5. 0 135
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_InnerJoin_BySelectMany_Test.cs
  6. 0 470
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_Test.cs
  7. 0 133
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Transaction_Test.cs
  8. 5 0
      src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Vit.Orm.Mysql.MsTest.csproj
  9. 5 3
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/DbContext_Extensions.cs
  10. 75 0
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTransactionScope.cs
  11. 279 0
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTranslateService.cs
  12. 0 93
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTranslator.cs
  13. 59 0
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/TranslateService/ExecuteDeleteTranslateService.cs
  14. 84 0
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/TranslateService/ExecuteUpdateTranslateService.cs
  15. 0 66
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/Translator/ExecuteDeleteTranslator.cs
  16. 6 3
      src/Vit.Orm.Mysql/Vit.Orm.Mysql/Vit.Orm.Mysql.csproj
  17. 10 12
      src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/DataSource.cs
  18. 48 0
      src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/DbFunction_Test.cs
  19. 5 0
      src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/Vit.Orm.SqlServer.MsTest.csproj
  20. 5 3
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/DbContext_Extensions.cs
  21. 77 0
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTransactionScope.cs
  22. 175 0
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/BaseQueryTranslateService.cs
  23. 59 0
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/ExecuteDeleteTranslateService.cs
  24. 85 0
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/ExecuteUpdateTranslateService.cs
  25. 66 0
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/QueryTranslateService.cs
  26. 245 0
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslateService.cs
  27. 0 128
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslator.cs
  28. 0 66
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/Translator/ExecuteDeleteTranslator.cs
  29. 0 91
      src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/Translator/ExecuteUpdateTranslator.cs
  30. 0 279
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CRUD_Test.cs
  31. 44 42
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/CRUD_Test.cs
  32. 16 11
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_Group_Test.cs
  33. 4 4
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_InnerJoin_ByJoin_Test.cs
  34. 4 4
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_InnerJoin_BySelectMany_Test.cs
  35. 3 3
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_LeftJoin_ByGroupJoin_Test.cs
  36. 12 12
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_LeftJoin_BySelectMany_Test.cs
  37. 32 71
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_Test.cs
  38. 53 53
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Transaction_Test.cs
  39. 6 14
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/DataSource.cs
  40. 56 0
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/DbFunction_Test.cs
  41. 0 107
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_LeftJoin_ByGroupJoin_Test.cs
  42. 0 200
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_LeftJoin_BySelectMany_Test.cs
  43. 0 470
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_Test.cs
  44. 1 0
      src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Vit.Orm.Sqlite.MsTest.csproj
  45. 5 3
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/DbContext_Extensions.cs
  46. 75 0
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTransactionScope.cs
  47. 261 0
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTranslateService.cs
  48. 0 139
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTranslator.cs
  49. 59 0
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/TranslateService/ExecuteDeleteTranslateService.cs
  50. 22 25
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/TranslateService/ExecuteUpdateTranslateService.cs
  51. 0 66
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/Translator/ExecuteDeleteTranslator.cs
  52. 0 91
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/Translator/ExecuteUpdateTranslator.cs
  53. 1 1
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/Vit.Orm.Sqlite.csproj
  54. 18 15
      src/Vit.Orm/DbContext.cs
  55. 8 4
      src/Vit.Orm/Entity/Dapper/EntityDescriptor.cs
  56. 5 1
      src/Vit.Orm/Entity/IEntityDescriptor.cs
  57. 38 75
      src/Vit.Orm/Sql/DataReader/EntityReader.cs
  58. 3 7
      src/Vit.Orm/Sql/DataReader/EntityReader/ModelReader.cs
  59. 327 56
      src/Vit.Orm/Sql/SqlDbContext.cs
  60. 13 239
      src/Vit.Orm/Sql/SqlDbSet.cs
  61. 220 0
      src/Vit.Orm/Sql/SqlTranslate/BaseQueryTranslateService.cs
  62. 10 0
      src/Vit.Orm/Sql/SqlTranslate/IQueryTranslateService.cs
  63. 18 22
      src/Vit.Orm/Sql/SqlTranslate/ISqlTranslateService.cs
  64. 30 0
      src/Vit.Orm/Sql/SqlTranslate/QueryTranslateArgument.cs
  65. 66 0
      src/Vit.Orm/Sql/SqlTranslate/QueryTranslateService.cs
  66. 20 0
      src/Vit.Orm/Sql/SqlTranslate/SqlTranslateArgument.cs
  67. 387 0
      src/Vit.Orm/Sql/SqlTranslate/SqlTranslateService.cs
  68. 40 0
      src/Vit.Orm/Sql/Transaction/DbTransactionWrap.cs
  69. 11 0
      src/Vit.Orm/Sql/Transaction/ITransactionScope.cs
  70. 55 0
      src/Vit.Orm/Sql/Transaction/SqlTransactionScope.cs
  71. 0 354
      src/Vit.Orm/Sql/Translator/BaseQueryTranslator.cs
  72. 0 130
      src/Vit.Orm/Sql/Translator/QueryTranslator.cs
  73. 0 256
      src/Vit.Orm/Sql/Translator/SqlTranslator.cs

+ 4 - 10
README.md

@@ -1,6 +1,6 @@
 
 # Vit.Orm
-Compatible with QueryBuilder ( ref to DynamicQueryable )
+Vitorm: an simple orm by Vit.Linq
 >source address: [https://github.com/serset/Vit.Orm](https://github.com/serset/Vit.Orm "https://github.com/serset/Vit.Orm")    
 
 ![](https://img.shields.io/github/license/Serset/Vit.Orm.svg)  
@@ -20,26 +20,20 @@ sqlite/transactions  https://learn.microsoft.com/en-us/dotnet/standard/data/sqli
 --------------
 # cur
 
-# support Mysql
-
+# support ElasticSearch
+# support ClickHouse
 
+# support Mysql
 
-# sqlite upgrade to latest version
 # remove depency of Dapper
 # try to make it clean
 
 
-# support SqlServer
-# support ElasticSearch
-# support ClickHouse
-
-
 # DbContext.QueryProcedure<Entity>(arg)
 
 --------------
 # TODO
 
-# sqlite nested transaction
 # Save SaveRange
 
 

+ 17 - 16
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/DataSource.cs

@@ -2,10 +2,9 @@
 
 using Vit.Orm.Sql;
 using Vit.Extensions;
-using Vit.Core.Module.Serialization;
 using Vit.Core.Util.ConfigurationManager;
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest
 {
     [Table("User")]
     public class User
@@ -24,33 +23,35 @@ namespace Vit.Orm.Sqlite.MsTest
     public class DataSource
     {
         static string connectionString = Appsettings.json.GetStringByPath("App.Db.ConnectionString");
-        public static DbContext CreateDbContext(string dbName = "DataSource")
-        { 
-            var dbContext = new SqlDbContext();
-            dbContext.UseMysql(connectionString);
-            return dbContext; 
-        }
  
-        public static DbContext CreateFormatedDbContext(string dbName = "DataSource")
+        public static SqlDbContext CreateDbContext()
         {
             var dbContext = new SqlDbContext();
             dbContext.UseMysql(connectionString);
 
+            dbContext.BeginTransaction();
+
             var userSet = dbContext.DbSet<User>();
+
+            dbContext.Execute(sql: "DROP TABLE  if exists `User`;");
+
             userSet.Create();
 
             var users = new List<User> {
-                    new User { id=1, name="u1", fatherId=4, motherId=6 },
-                    new User { id=2, name="u2", fatherId=4, motherId=6 },
-                    new User { id=3, name="u3", fatherId=5, motherId=6 },
-                    new User { id=4, name="u4" },
-                    new User { id=5, name="u5" },
-                    new User { id=6, name="u6" },
+                    new User {   name="u1", fatherId=4, motherId=6 },
+                    new User {   name="u2", fatherId=4, motherId=6 },
+                    new User {   name="u3", fatherId=5, motherId=6 },
+                    new User {   name="u4" },
+                    new User {   name="u5" },
+                    new User {   name="u6" },
                 };
-            users.ForEach(user => { user.birth = DateTime.Parse("2021-01-01 00:00:00").AddHours(user.id); });
 
             dbContext.AddRange(users);
 
+            users.ForEach(user => { user.birth = DateTime.Parse("2021-01-01 00:00:00").AddHours(user.id); });
+
+            dbContext.UpdateRange(users);
+
             return dbContext;
         }
 

+ 0 - 181
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_Group_Test.cs

@@ -1,181 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-    [TestClass]
-    public class Query_Group_Test
-    {
-
-        [TestMethod]
-        public void Test_Group_Demo()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                        from user in userQuery
-                        group user by new { user.fatherId, user.motherId } into userGroup
-                        select new { userGroup.Key.fatherId, userGroup.Key.motherId  };
-            
-                var sql = query.ToExecuteString();
-                var rows = query.ToList();
-
-                Assert.AreEqual(3, rows.Count);
-                Assert.AreEqual(4, rows[1].fatherId);
-                Assert.AreEqual(6, rows[1].motherId);
-                Assert.AreEqual(5, rows[2].fatherId);
-                Assert.AreEqual(6, rows[2].motherId);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                        userQuery
-                        .GroupBy(user => new { user.fatherId, user.motherId })
-                        .Select(userGroup => new
-                        {
-                            userGroup.Key.fatherId,
-                            userGroup.Key.motherId
-                        })
-                        ;
-
-                var sql = query.ToExecuteString();
-                var rows = query.ToList();
-
-                Assert.AreEqual(3, rows.Count);
-                Assert.AreEqual(4, rows[1].fatherId);
-                Assert.AreEqual(6, rows[1].motherId);
-                Assert.AreEqual(5, rows[2].fatherId);
-                Assert.AreEqual(6, rows[2].motherId);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_Group_Complex()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                        from user in userQuery.Where(u => u.id > 1)
-                        group user by new { user.fatherId, user.motherId } into userGroup
-                        where userGroup.Key.motherId != null
-                        orderby userGroup.Key.fatherId descending
-                        select new { userGroup.Key.fatherId, userGroup.Key.motherId, rowCount = userGroup.Count(), maxId = userGroup.Max(m => m.id) };
-
-                query = query.Skip(1).Take(1);
-
-                var sql = query.ToExecuteString();
-                var rows = query.ToList();
-
-                Assert.AreEqual(1, rows.Count);
-                Assert.AreEqual(4, rows[0].fatherId);
-                Assert.AreEqual(6, rows[0].motherId);
-                Assert.AreEqual(1, rows[0].rowCount);
-                Assert.AreEqual(2, rows[0].maxId);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                        userQuery
-                        .Where(u => u.id > 1)
-                        .GroupBy(user => new { user.fatherId, user.motherId })
-                        .Where(userGroup => userGroup.Key.motherId != null)
-                        .OrderByDescending(userGroup => userGroup.Key.fatherId)
-                        .Select(userGroup => new
-                        {
-                            userGroup.Key.fatherId,
-                            userGroup.Key.motherId,
-                            rowCount = userGroup.Count(),
-                            maxId = userGroup.Max(m => m.id)
-                        })
-                        .Skip(1)
-                        .Take(1)
-                        ;
-
-                var sql = query.ToExecuteString();
-                var rows = query.ToList();
-
-                Assert.AreEqual(1, rows.Count);
-                Assert.AreEqual(4, rows[0].fatherId);
-                Assert.AreEqual(6, rows[0].motherId);
-                Assert.AreEqual(1, rows[0].rowCount);
-                Assert.AreEqual(2, rows[0].maxId);
-            }
-        }
-
-
-
-
-        [TestMethod]
-        public void Test_Others()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var query =
-                    userQuery
-                    .GroupBy(user => new { user.fatherId, user.motherId })
-                    .Select(userGroup => new
-                    {
-                        userGroup.Key.fatherId,
-                        rowCount = userGroup.Count(),
-                        maxId = userGroup.Max(m => m.id),
-                        minId = userGroup.Min(m => m.id),
-                        sumId = userGroup.Sum(m => m.id),
-                        avgId = userGroup.Average(m => m.id)
-                    })
-                    ;
-
-                var sql = query.ToExecuteString();
-                var rows = query.ToList();
-
-                Assert.AreEqual(3, rows.Count);
-                Assert.AreEqual(2, rows[1].rowCount);
-                Assert.AreEqual(1.5, rows[1].avgId);
-            }
-            {
-                var query =
-                    userQuery
-                    .GroupBy(user => new { user.fatherId, user.motherId })
-                    .Where(userGroup => userGroup.Key.motherId != null)
-                    .OrderByDescending(userGroup => userGroup.Key.fatherId)
-                    .Select(userGroup => new { userGroup.Key.fatherId, userGroup.Key.motherId })
-                    ;
-
-                var rows = query.ToList();
-                var sql = query.ToExecuteString();
-
-                Assert.AreEqual(2, rows.Count);
-                Assert.AreEqual(5, rows[0].fatherId);
-            }
-            {
-                var query =
-                    userQuery
-                    .GroupBy(user => user.fatherId)
-                    .Where(userGroup => userGroup.Key != null)
-                    .OrderByDescending(userGroup => userGroup.Key)
-                    .Select(userGroup => new { fatherId = userGroup.Key, rowCount = userGroup.Count() })
-                    ;
-
-                var rows = query.ToList();
-                var sql = query.ToExecuteString();
-
-                Assert.AreEqual(2, rows.Count);
-                Assert.AreEqual(5, rows[0].fatherId);
-            }
-        }
-
-
-    }
-}

+ 0 - 218
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_InnerJoin_ByJoin_Test.cs

@@ -1,218 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-    [TestClass]
-    public class Query_InnerJoin_ByJoin_Test
-    {
-
-        [TestMethod]
-        public void Test_InnerJoin_Demo()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    from father in userQuery.Where(father => user.fatherId == father.id)
-                    where user.id > 2
-                    select new { user, father };
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().user.id);
-                Assert.AreEqual(5, userList.First().father.id);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                    userQuery.SelectMany(
-                        user => userQuery.Where(father => user.fatherId == father.id)
-                        , (user, father) => new { user, father }
-                    )
-                    .Where(row => row.user.id > 2)
-                    .Select(row => new { row.user, row.father });
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().user.id);
-                Assert.AreEqual(5, userList.First().father.id);
-            }
-
-        }
-
-
-        [TestMethod]
-        public void Test_InnerJoin_Complex()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    join father in userQuery on user.fatherId equals father.id
-                    join mother in userQuery on user.motherId equals mother.id
-                    where user.id > 1
-                    orderby father.id descending
-                    select new
-                    {
-                        user,
-                        father,
-                        mother,
-                        testId = user.id + 100,
-                        hasFather = father != null ? true : false
-                    };
-                query = query.Skip(1).Take(1);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-
-                var first = userList.First();
-                Assert.AreEqual(2, first.user.id);
-                Assert.AreEqual(4, first.father.id);
-                Assert.AreEqual(6, first.mother.id);
-                Assert.AreEqual(102, first.testId);
-                Assert.AreEqual(true, first.hasFather);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                    userQuery.Join(
-                        userQuery
-                        , user => user.fatherId
-                        , father => father.id
-                        , (user, father) => new { user, father }
-                    ).Join(
-                        userQuery
-                        , row => row.user.motherId
-                        , mother => mother.id
-                        , (row, mother) => new { row, mother }
-                    )
-                    .Where(row2 => row2.row.user.id > 1)
-                    .OrderByDescending(row2 => row2.row.father.id)
-                    .Select(row2 =>
-                        new
-                        {
-                            row2.row.user,
-                            row2.row.father,
-                            row2.mother,
-                            testId = row2.row.user.id + 100,
-                            hasFather = row2.row.father != null ? true : false
-                        }
-                    );
-
-                query = query.Skip(1).Take(1);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-
-                var first = userList.First();
-                Assert.AreEqual(2, first.user.id);
-                Assert.AreEqual(4, first.father.id);
-                Assert.AreEqual(6, first.mother.id);
-                Assert.AreEqual(102, first.testId);
-                Assert.AreEqual(true, first.hasFather);
-            }
-        }
-
-
-
-        [TestMethod]
-        public void Test_InnerJoin_Others()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // simple
-            {
-                var query =
-                    userQuery.Join(
-                        userQuery
-                        , user => user.fatherId
-                        , father => father.id
-                        , (user, father) => new { user, father }
-                    );
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(1, userList.First().user.id);
-            }
-
-            // where
-            {
-                var query =
-                    userQuery.Join(
-                        userQuery
-                        , user => user.fatherId
-                        , father => father.id
-                        , (user, father) => new { user, father }
-                    ).Where(row => row.user.id > 2);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().user.id);
-            }
-            // select
-            {
-                var query =
-                    userQuery.Join(
-                        userQuery
-                        , user => user.fatherId
-                        , father => father.id
-                        , (user, father) => new { user, father }
-                    ).Where(row => row.user.id > 2)
-                    .Select(row => new { userId = row.user.id, fatherId = row.father.id });
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().userId);
-                Assert.AreEqual(5, userList.First().fatherId);
-            }
-            // full feature
-            {
-                var query =
-                         from user in userQuery
-                         join father in userQuery on user.fatherId equals father.id
-                         where user.id > 1
-                         orderby user.id descending
-                         select new { user, father };
-
-                query = query.Skip(1).Take(1);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(2, userList.First().user.id);
-            }
-
-        }
-
-
-
-
-    }
-}

+ 0 - 135
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_InnerJoin_BySelectMany_Test.cs

@@ -1,135 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-    [TestClass]
-    public class Query_InnerJoin_BySelectMany_Test
-    {
-
-        [TestMethod]
-        public void Test_InnerJoin_Demo()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    from father in userQuery.Where(father => user.fatherId == father.id)
-                    where user.id > 2
-                    select new { user, father };
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().user.id);
-                Assert.AreEqual(5, userList.First().father.id);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                    userQuery.SelectMany(
-                        user => userQuery.Where(father => user.fatherId == father.id)
-                        , (user, father) => new { user, father }
-                    )
-                    .Where(row => row.user.id > 2)
-                    .Select(row => new { row.user, row.father });
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().user.id);
-                Assert.AreEqual(5, userList.First().father.id);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_InnerJoin_Complex()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    from father in userQuery.Where(father => user.fatherId == father.id)
-                    from mother in userQuery.Where(mother => user.motherId == mother.id)
-                    where user.id > 1
-                    orderby father.id descending
-                    select new
-                    {
-                        user,
-                        father,
-                        mother,
-                        testId = user.id + 100,
-                        hasFather = father != null ? true : false
-                    };
-                query = query.Skip(1).Take(1);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-
-                var first = userList.First();
-                Assert.AreEqual(2, first.user.id);
-                Assert.AreEqual(4, first.father.id);
-                Assert.AreEqual(6, first.mother.id);
-                Assert.AreEqual(102, first.testId);
-                Assert.AreEqual(true, first.hasFather);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                    userQuery.SelectMany(
-                        user => userQuery.Where(father => user.fatherId == father.id)
-                        , (user, father) => new { user, father }
-                    ).SelectMany(
-                        row => userQuery.Where(mother => row.user.motherId == mother.id)
-                        , (row, mother) => new { row, mother }
-                    )
-                    .Where(row2 => row2.row.user.id > 1)
-                    .OrderByDescending(row2 => row2.row.father.id)
-                    .Select(row2 =>
-                        new
-                        {
-                            row2.row.user,
-                            row2.row.father,
-                            row2.mother,
-                            testId = row2.row.user.id + 100,
-                            hasFather = row2.row.father != null ? true : false
-                        }
-                    );
-
-                query = query.Skip(1).Take(1);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-
-                var first = userList.First();
-                Assert.AreEqual(2, first.user.id);
-                Assert.AreEqual(4, first.father.id);
-                Assert.AreEqual(6, first.mother.id);
-                Assert.AreEqual(102, first.testId);
-                Assert.AreEqual(true, first.hasFather);
-            }
-        }
-
-
- 
-
-
-    }
-}

+ 0 - 470
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_Test.cs

@@ -1,470 +0,0 @@
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-using static Vit.Core.Util.XmlComment.MethodComment;
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-
-    [TestClass]
-    public class Query_Test
-    {
-        [TestMethod]
-        public void Test_Get()
-        {
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var user = dbContext.Get<User>(3);
-                Assert.AreEqual(3, user?.id);
-            }
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var user = dbContext.DbSet<User>().Get(5);
-                Assert.AreEqual(5, user?.id);
-            }
-        }
-
-
-
-
-        [TestMethod]
-        public void Test_PlainQuery()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-                Assert.AreEqual(6, userList.Last().id);
-            }
-
-
-            {
-                var userList = userQuery.Select(u => u.id).ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First());
-                Assert.AreEqual(6, userList.Last());
-            }
-        }
-
-
-
-        [TestMethod]
-        public void Test_Where()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.Where(u => u.id > 2).Where(m => m.id < 4).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-            {
-                var userList = userQuery.Where(u => u.id + 1 == 4).Where(m => m.fatherId == 5).ToList();
-                Assert.AreEqual(3, userList.First().id);
-            }
-            {
-                var userList = userQuery.Where(u => 4 == u.id + 1).Where(m => m.fatherId == 5).ToList();
-                Assert.AreEqual(3, userList.First().id);
-            }
-
-
-            {
-                var userList = userQuery.Where(u => u.birth == new DateTime(2021, 01, 01, 03, 00, 00)).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-            {
-                var userList = userQuery.Where(u => u.birth == DateTime.Parse("2021-01-01 01:00:00").AddHours(2)).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-        }
-
-        [TestMethod]
-        public void Test_Select()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.Select(u => u).Where(user => user.id > 2).Where(user => user.id < 4).Select(u => u).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-
-
-            {
-                var userList = (from user in userQuery
-                                select new
-                                {
-                                    uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
-                                    uniqueId2 = $"{user.id}_{user.fatherId}_{user.motherId}"
-                                }).ToList();
-
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual("1_4_6", userList.First().uniqueId1);
-            }
-
-        }
-
-        [TestMethod]
-        public void Test_Count()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var count = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id > 2 && father == null
-                             select new
-                             {
-                                 father
-                             }).Count();
-
-                Assert.AreEqual(3, count);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_AllFeatures()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            #region SelectMany().Where().OrderBy().Skip().Take().ToExecuteString()
-            /*
-            users.SelectMany(
-                user => users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                , (user, father) => new {user = user, father = father}
-            ).Where(row => row.user.id > 2)
-            .Select(row => new {row.user })
-            .OrderBy(user=>user.id)
-            .Skip(1).Take(2);
-             */
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id > 2
-                             orderby father.id, user.id descending
-                             select new
-                             {
-                                 user
-                             })
-                            .Skip(1).Take(2);
-
-                var sql = query.ToExecuteString();
-                var list = query.ToList();
-
-                Assert.AreEqual(2, list.Count);
-                Assert.AreEqual(5, list[0].user.id);
-                Assert.AreEqual(4, list[1].user.id);
-            }
-            #endregion
-        }
-
-
-
-        [TestMethod]
-        public void Test_FirstOrDefault()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.FirstOrDefault();
-                Assert.AreEqual(1, user?.id);
-            }
-
-            {
-                var user = userQuery.FirstOrDefault(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                var user = userQuery.FirstOrDefault(user => user.id == 13);
-                Assert.AreEqual(null, user?.id);
-            }
-
-            {
-                var user = userQuery.OrderByDescending(m => m.id).FirstOrDefault();
-                Assert.AreEqual(6, user?.id);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_First()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.First();
-                Assert.AreEqual(1, user?.id);
-            }
-
-            {
-                var user = userQuery.First(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                try
-                {
-                    var user = userQuery.First(user => user.id == 13);
-                    Assert.Fail("IQueryalbe.First should throw Exception");
-                }
-                catch (Exception ex)
-                {
-                }
-
-            }
-
-        }
-
-
-
-        [TestMethod]
-        public void Test_LastOrDefault()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.LastOrDefault();
-                Assert.AreEqual(6, user?.id);
-            }
-
-            {
-                var user = userQuery.LastOrDefault(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                var user = userQuery.LastOrDefault(user => user.id == 13);
-                Assert.AreEqual(null, user?.id);
-            }
-
-            {
-                var user = userQuery.OrderByDescending(m => m.id).LastOrDefault();
-                Assert.AreEqual(1, user?.id);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_Last()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.Last();
-                Assert.AreEqual(6, user?.id);
-            }
-
-            {
-                var user = userQuery.Last(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                try
-                {
-                    var user = userQuery.Last(user => user.id == 13);
-                    Assert.Fail("IQueryalbe.First should throw Exception");
-                }
-                catch (Exception ex)
-                {
-                }
-
-            }
-
-        }
-
-        // Enumerable.ToArray
-        [TestMethod]
-        public void Test_Enumerable_ToArray()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.ToArray();
-                Assert.AreEqual(6, userList.Length);
-                Assert.AreEqual(1, userList.First().id);
-                Assert.AreEqual(6, userList.Last().id);
-            }
-
-
-            {
-                var userList = userQuery.Select(u => u.id).ToArray();
-                Assert.AreEqual(6, userList.Length);
-                Assert.AreEqual(1, userList.First());
-                Assert.AreEqual(6, userList.Last());
-            }
-        }
-
-
-
-        // Enumerable.Contains
-        // Queryable.Contains
-        [TestMethod]
-        public void Test_Contains()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.Where(u => new[] { 3, 5 }.Contains(u.id)).ToList();
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual(5, userList.Last().id);
-            }
-            {
-                var ids = new[] { 3, 5 }.AsEnumerable();
-                var userList = userQuery.Where(u => ids.Contains(u.id)).ToList();
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual(5, userList.Last().id);
-            }
-            {
-                var ids = new[] { 3, 5 }.AsQueryable();
-                var userList = userQuery.Where(u => ids.Contains(u.id)).ToList();
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual(5, userList.Last().id);
-            }
-        }
-
-        [TestMethod]
-        public void Test_StringMethods()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            userQuery.ExecuteUpdate(row => new User
-            {
-                name = "u|" + row.id + "|" + (row.fatherId.ToString() ?? "") + "|" + (row.motherId.ToString() ?? "")
-            });
-
-            // StartsWith
-            {
-                var query = userQuery.Where(u => u.name.StartsWith("u|3|5"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual("u|3|5|6", userList.First().name);
-            }
-            // EndsWith
-            {
-                var query = userQuery.Where(u => u.name.EndsWith("3|5|6"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual("u|3|5|6", userList.First().name);
-            }
-            // Contains
-            {
-                var query = userQuery.Where(u => u.name.Contains("|3|5|"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual("u|3|5|6", userList.First().name);
-            }
-        }
-
-        [TestMethod]
-        public void Test_DbFunction()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-
-            // select * from `User` as t0  where IIF(`t0`.`fatherId` is not null,true, false)
-            {
-                var query = userQuery.Where(u => DbFunction.Call<bool>("IIF", u.fatherId != null, true, false));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(3, userList.Last().id);
-            }
-
-            {
-                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime?>("datetime", "2021-01-01 00:00:00", "+2 hours"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(2, userList.First().id);
-            }
-            {
-                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime>("datetime", "2021-01-01 00:00:00", "+" + u.id + " hours"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-            }
-
-            // coalesce(parameter1,parameter2, …)
-            {
-                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-            }
-
-
-        }
-
-
-
-        [TestMethod]
-        public void Test_Distinct()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var query = userQuery.Select(u => new { u.fatherId }).Distinct();
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(4, userList.First().fatherId);
-                Assert.AreEqual(null, userList.Last().fatherId);
-            }
-            {
-                var query = userQuery.Select(u => u.fatherId).Distinct();
-
-                var sql = query.ToExecuteString();
-                var fatherId = query.FirstOrDefault();
-                Assert.AreEqual(4, fatherId);
-            }
-            {
-                var query = userQuery.Distinct();
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(6, userList.Count);
-            }
-
-        }
-
-
-
-
-    }
-}

+ 0 - 133
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Transaction_Test.cs

@@ -1,133 +0,0 @@
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-
-    [TestClass]
-    public class Transaction_Test
-    {
-
-        [TestMethod]
-        public void Test_Transaction()
-        {
-            var dbName = System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_";
-            DataSource.CreateFormatedDbContext(dbName).Dispose();
-
-
-            #region Transaction
-            {
-                using var dbContext = DataSource.CreateDbContext(dbName);
-                var userSet = dbContext.DbSet<User>();
-
-                Assert.AreEqual("u4", userSet.Get(4).name);
-
-                dbContext.Update(new User { id = 4, name = "u41" });
-                Assert.AreEqual("u41", userSet.Get(4).name);
-
-                using (var tran2 = dbContext.BeginTransaction())
-                {
-                    dbContext.Update(new User { id = 4, name = "u42" });
-                    Assert.AreEqual("u42", userSet.Get(4).name);
-                    //tran2.Rollback();
-                }
-
-                Assert.AreEqual("u41", userSet.Get(4).name);
-                using (var tran3 = dbContext.BeginTransaction())
-                {
-                    dbContext.Update(new User { id = 4, name = "u43" });
-                    Assert.AreEqual("u43", userSet.Get(4).name);
-
-                    tran3.Commit();
-                }
-
-                Assert.AreEqual("u43", userSet.Get(4).name);
-            }
-            #endregion
-
-
-            #region Transaction Dispose
-            {
-                {
-                    using var dbContext = DataSource.CreateDbContext(dbName);
-                    var userSet = dbContext.DbSet<User>();
-
-                    var tran2 = dbContext.BeginTransaction();
-                    {
-                        dbContext.Update(new User { id = 4, name = "u42" });
-                        Assert.AreEqual("u42", userSet.Get(4).name);
-                        tran2.Commit();
-                    }
-
-                    Assert.AreEqual("u42", userSet.Get(4).name);
-
-                    var tran3 = dbContext.BeginTransaction();
-                    {
-                        dbContext.Update(new User { id = 4, name = "u43" });
-                        Assert.AreEqual("u43", userSet.Get(4).name);
-                    }
-                    Assert.AreEqual("u43", userSet.Get(4).name);
-                }
-                {
-                    using var dbContext = DataSource.CreateDbContext(dbName);
-                    var userSet = dbContext.DbSet<User>();
-
-                    Assert.AreEqual("u42", userSet.Get(4).name);
-                }
-            }
-            #endregion
-
-
-
-        }
-
-
-
-
-        //[TestMethod]
-        public void Test_NestedTransaction()
-        {
-            var dbName = System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_";
-
-            #region
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(dbName);
-                var userSet = dbContext.DbSet<User>();
-
-                using (var tran1 = dbContext.BeginTransaction())
-                {
-                    Assert.AreEqual("u4", userSet.Get(4).name);
-
-                    dbContext.Update(new User { id = 4, name = "u41" });
-                    Assert.AreEqual("u41", userSet.Get(4).name);
-
-                    using (var tran2 = dbContext.BeginTransaction())
-                    {
-                        dbContext.Update(new User { id = 4, name = "u42" });
-                        Assert.AreEqual("u42", userSet.Get(4).name);
-                        tran2.Rollback();
-                    }
-
-                    Assert.AreEqual("u41", userSet.Get(4).name);
-                    using (var tran3 = dbContext.BeginTransaction())
-                    {
-                        dbContext.Update(new User { id = 4, name = "u43" });
-                        Assert.AreEqual("u43", userSet.Get(4).name);
-                    }
-
-                    Assert.AreEqual("u43", userSet.Get(4).name);
-                }
-
-                Assert.AreEqual("u43", userSet.Get(4).name);
-            }
-            #endregion
-
-
-
-        }
-
-
-
-
-    }
-}

+ 5 - 0
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Vit.Orm.Mysql.MsTest.csproj

@@ -27,6 +27,11 @@
         </None>
     </ItemGroup>
 
+    <ItemGroup>
+        <Compile Include="..\..\..\Vit.Orm.Sqlite\Test\Vit.Orm.Sqlite.MsTest\CommonTest\*.cs" Link="CommonTest\%(RecursiveDir)%(FileName)%(Extension)" />
+        <!--<Compile Include="..\Vit.Linq.MsTest\DataSource.cs" />-->
+    </ItemGroup>
+
 
 
 </Project>

+ 5 - 3
src/Vit.Orm.Mysql/Vit.Orm.Mysql/DbContext_Extensions.cs

@@ -4,7 +4,7 @@ using System.Data;
 using Vit.Orm.Entity;
 using Vit.Orm.Entity.Dapper;
 using Vit.Orm.Sql;
-using Vit.Orm.Mysql;
+using Vit.Orm.Sql.SqlTranslate;
 
 namespace Vit.Extensions
 {
@@ -12,14 +12,16 @@ namespace Vit.Extensions
     {
         public static SqlDbContext UseMysql(this SqlDbContext dbContext, string ConnectionString)
         {
-            ISqlTranslator sqlTranslator = new SqlTranslator(dbContext);
+            ISqlTranslateService sqlTranslateService =   Vit.Orm.Mysql.SqlTranslateService.Instance;
 
             Func<IDbConnection> createDbConnection = () => new MySql.Data.MySqlClient.MySqlConnection(ConnectionString);
 
             Func<Type, IEntityDescriptor> getEntityDescriptor = (type) => EntityDescriptor.GetEntityDescriptor(type);
 
 
-            dbContext.Init(sqlTranslator: sqlTranslator, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
+            dbContext.Init(sqlTranslateService: sqlTranslateService, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
+
+            //dbContext.createTransactionScope = (dbContext) => new Vit.Orm.Mysql.SqlTransactionScope(dbContext);
 
             return dbContext;
         }

+ 75 - 0
src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTransactionScope.cs

@@ -0,0 +1,75 @@
+using System;
+using System.Data;
+
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.Transaction;
+
+using SqlTransaction = MySql.Data.MySqlClient.MySqlTransaction;
+
+namespace Vit.Orm.Mysql
+{
+    public class SqlTransactionScope : Vit.Orm.Sql.Transaction.SqlTransactionScope
+    {
+        int savePointCount = 0;
+        public DbTransactionWrap CreateTransactionSavePoint(IDbTransaction originalTransaction)
+        {
+            var savePointName = "tran" + savePointCount++;
+            return new DbTransactionWrapSavePoint(originalTransaction, savePointName);
+        }
+        public SqlTransactionScope(SqlDbContext dbContext) : base(dbContext)
+        {
+        }
+
+        public override IDbTransaction BeginTransaction()
+        {
+            DbTransactionWrap transactionWrap;
+            IDbTransaction originalTransaction = GetCurrentTransaction();
+            if (originalTransaction == null)
+            {
+                var dbConnection = dbContext.dbConnection;
+                if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
+                originalTransaction = dbConnection.BeginTransaction();
+
+                transactionWrap = new DbTransactionWrap(originalTransaction);
+            }
+            else
+            {
+                transactionWrap = CreateTransactionSavePoint(originalTransaction);
+            }
+
+            transactions.Push(transactionWrap);
+            return transactionWrap;
+        }
+
+    }
+
+    public class DbTransactionWrapSavePoint : DbTransactionWrap
+    {
+        public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
+        string savePoint;
+        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePoint) : base(transaction)
+        {
+            this.savePoint = savePoint;
+            sqlTran.Save(savePoint);
+        }
+
+        public override void Commit()
+        {
+            sqlTran.Release(savePoint);
+            TransactionState = ETransactionState.Committed;
+        }
+
+        public override void Dispose()
+        {
+            if (TransactionState == ETransactionState.Active)
+                sqlTran.Rollback(savePoint);
+            TransactionState = ETransactionState.Disposed;
+        }
+
+        public override void Rollback()
+        {
+            sqlTran.Rollback(savePoint);
+            TransactionState = ETransactionState.RolledBack;
+        }
+    }
+}

+ 279 - 0
src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTranslateService.cs

@@ -0,0 +1,279 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.Entity;
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.SqlTranslate;
+using Vit.Orm.Mysql.TranslateService;
+
+
+namespace Vit.Orm.Mysql
+{
+    public class SqlTranslateService : Vit.Orm.Sql.SqlTranslate.SqlTranslateService
+    {
+        public static readonly SqlTranslateService Instance = new SqlTranslateService();
+
+        protected QueryTranslateService queryTranslateService;
+        protected ExecuteUpdateTranslateService executeUpdateTranslateService;
+        protected ExecuteDeleteTranslateService executeDeleteTranslateService;
+
+        public SqlTranslateService()
+        {
+            queryTranslateService = new QueryTranslateService(this);
+            executeUpdateTranslateService = new ExecuteUpdateTranslateService(this);
+            executeDeleteTranslateService = new ExecuteDeleteTranslateService(this);
+        }
+        /// <summary>
+        ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to delimit.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public override string DelimitIdentifier(string identifier) => $"`{EscapeIdentifier(identifier)}`"; // Interpolation okay; strings
+
+        /// <summary>
+        ///     Generates the escaped SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to be escaped.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public override string EscapeIdentifier(string identifier) => identifier.Replace("`", "\\`");
+
+
+        #region EvalExpression
+        /// <summary>
+        /// read where or value or on
+        /// </summary>
+        /// <param name="arg"></param>
+        /// <returns></returns>
+        /// <exception cref="NotSupportedException"></exception>
+        /// <param name="data"></param>
+        public override string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
+        {
+            switch (data.nodeType)
+            {
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall methodCall = data;
+                        switch (methodCall.methodName)
+                        {
+                            // ##1 ToString
+                            case nameof(object.ToString):
+                                {
+                                    return $"cast({EvalExpression(arg, methodCall.@object)} as varchar(1000))";
+                                }
+
+                            #region ##2 String method:  StartsWith EndsWith Contains
+                            case nameof(string.StartsWith): // String.StartsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like {EvalExpression(arg, value)}+'%'";
+                                }
+                            case nameof(string.EndsWith): // String.EndsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}";
+                                }
+                            case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}+'%'";
+                                }
+                                #endregion
+                        }
+                        break;
+                    }
+
+                #region Read Value
+                case NodeType.Convert:
+                    {
+                        // cast( 4.1 as signed)
+
+                        ExpressionNode_Convert convert = data;
+
+                        Type targetType = convert.valueType?.ToType();
+
+                        if (targetType == typeof(object)) return EvalExpression(arg, convert.body);
+
+                        // Nullable
+                        if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
+
+                        string targetDbType = GetDbType(targetType);
+
+                        var sourceType = convert.body.Member_GetType();
+                        if (sourceType != null)
+                        {
+                            if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
+
+                            if (targetDbType == GetDbType(sourceType)) return EvalExpression(arg, convert.body);
+                        }
+                        return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
+                    }
+                case nameof(ExpressionType.Add):
+                    {
+                        ExpressionNode_Binary binary = data;
+
+                        // ##1 String Add
+                        if (data.valueType?.ToType() == typeof(string))
+                        {
+                            return $"CONCAT({EvalExpression(arg, binary.left)} ,{EvalExpression(arg, binary.right)})";
+                        }
+
+                        // ##2 Numberic Add
+                        return $"{EvalExpression(arg, binary.left)} + {EvalExpression(arg, binary.right)}";
+                    }
+                case nameof(ExpressionType.Coalesce):
+                    {
+                        ExpressionNode_Binary binary = data;
+                        return $"COALESCE({EvalExpression(arg, binary.left)},{EvalExpression(arg, binary.right)})";
+                    }
+                    #endregion
+
+            }
+
+            return base.EvalExpression(arg, data);
+        }
+        #endregion
+
+
+
+        #region PrepareCreate
+        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
+        {
+            /* //sql
+CREATE TABLE user (
+  id int NOT NULL PRIMARY KEY,
+  name varchar(100) DEFAULT NULL,
+  birth date DEFAULT NULL,
+  fatherId int DEFAULT NULL,
+  motherId int DEFAULT NULL
+) ;
+              */
+            List<string> sqlFields = new();
+
+            // #1 primary key
+            sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY  AUTO_INCREMENT");
+
+            // #2 columns
+            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+
+            return $@"
+CREATE TABLE {DelimitIdentifier(entityDescriptor.tableName)} (
+{string.Join(",\r\n  ", sqlFields)}
+)";
+
+
+            string GetColumnSql(IColumnDescriptor column)
+            {
+                bool nullable = false;
+
+                var type = column.type;
+                if (type.IsGenericType || type == typeof(string))
+                {
+                    nullable = true;
+                    type = TypeUtil.GetUnderlyingType(type);
+                }
+                // name varchar(100) DEFAULT NULL
+                return $"  {DelimitIdentifier(column.name)} {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
+            }
+        }
+        protected override string GetDbType(Type type)
+        {
+            if (type == typeof(DateTime))
+                return "DATETIME";
+
+            if (type == typeof(string))
+                return "varchar(1000)";
+
+            if (type == typeof(float)) return "FLOAT";
+            if (type == typeof(double) || type == typeof(decimal))
+                return "DOUBLE";
+
+            if (type == typeof(Int32)) return "INTEGER";
+            if (type == typeof(Int16)) return "SMALLINT";
+            if (type == typeof(byte)) return "TINYINT";
+            if (type == typeof(bool)) return "TINYINT";
+
+            if (type.Name.ToLower().Contains("int")) return "INTEGER";
+
+            throw new NotSupportedException("unsupported column type:" + type.Name);
+        }
+        #endregion
+
+
+        public override (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg)
+        {
+            /* //sql
+             insert into user(name,birth,fatherId,motherId) values('','','');
+             select seq from sqlite_sequence where name='user';
+              */
+            var entityDescriptor = arg.entityDescriptor;
+
+            var columns = entityDescriptor.allColumns;
+
+            // #1 GetSqlParams 
+            Func<object, Dictionary<string, object>> GetSqlParams = (entity) =>
+            {
+                var sqlParam = new Dictionary<string, object>();
+                foreach (var column in columns)
+                {
+                    var columnName = column.name;
+                    var value = column.Get(entity);
+
+                    sqlParam[columnName] = value;
+                }
+                return sqlParam;
+            };
+
+            #region #2 columns 
+            List<string> columnNames = new List<string>();
+            List<string> valueParams = new List<string>();
+            string columnName;
+
+            foreach (var column in columns)
+            {
+                columnName = column.name;
+
+                columnNames.Add(DelimitIdentifier(columnName));
+                valueParams.Add(GenerateParameterName(columnName));
+            }
+            #endregion
+
+            // #3 build sql
+            string sql = $@"insert into {DelimitIdentifier(entityDescriptor.tableName)}({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
+            sql += "select last_insert_id();";
+            return (sql, GetSqlParams);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = queryTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam, arg.dataReader);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeUpdateTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeDeleteTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+
+
+    }
+}

+ 0 - 93
src/Vit.Orm.Mysql/Vit.Orm.Mysql/SqlTranslator.cs

@@ -1,93 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Extensions.Linq_Extensions;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Orm.Entity;
-using Vit.Orm.Mysql.Translator;
-
-
-namespace Vit.Orm.Mysql
-{
-    public class SqlTranslator : Vit.Orm.Sql.Translator.SqlTranslator
-    {
-        public SqlTranslator(DbContext dbContext):base(dbContext) 
-        {
-        }
-
-        #region PrepareCreate
-        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
-        {
-            /* //sql
-CREATE TABLE user (
-  id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  name varchar(100) DEFAULT NULL,
-  birth date DEFAULT NULL,
-  fatherId int DEFAULT NULL,
-  motherId int DEFAULT NULL
-) ;
-              */
-            List<string> sqlFields = new();
-
-            // #1 primary key
-            sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY AUTO_INCREMENT");
-
-            // #2 columns
-            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
-
-            return $@"
-CREATE TABLE {DelimitIdentifier(entityDescriptor.tableName)} (
-{string.Join(",\r\n  ", sqlFields)}
-)";
-
-
-            string GetColumnSql(IColumnDescriptor column)
-            {
-                bool nullable = false;
-
-                var type = column.type;
-                if (type.IsGenericType)
-                {
-                    nullable = true;
-                    type = type.GetGenericArguments()[0];
-                }
-                // name varchar(100) DEFAULT NULL
-                return $"  {DelimitIdentifier(column.name)} {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
-            }
-        }
-        protected override string GetDbType(Type type)
-        {
-            if (type == typeof(DateTime))
-                return "DATETIME";
-
-            if (type == typeof(string))
-                return "TEXT";
-
-            if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
-                return "REAL";
-
-            if (type == typeof(bool) || type.Name.ToLower().Contains("int")) return "INTEGER";
-
-            throw new NotSupportedException("unsupported column type:" + type.Name);
-        }
-        #endregion
-
-
-        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(CombinedStream combinedStream)
-        {
-            var query = new ExecuteUpdateTranslator(this);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam);
-        }
-
-        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(CombinedStream combinedStream)
-        {
-            var query = new ExecuteDeleteTranslator(this);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam);
-        }
-
-
-
-    }
-}

+ 59 - 0
src/Vit.Orm.Mysql/Vit.Orm.Mysql/TranslateService/ExecuteDeleteTranslateService.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.Mysql.TranslateService
+{
+    public class ExecuteDeleteTranslateService : BaseQueryTranslateService
+    {
+        /*
+WITH tmp AS (
+    select u.id 
+    from User u
+    left join User father on u.fatherId = father.id 
+    where u.id > 0
+)
+delete from User where id in ( SELECT id FROM tmp );
+         */
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            var sqlInner = base.BuildQuery(arg, stream);
+
+
+            var NewLine = "\r\n";
+            var keyName = entityDescriptor.keyName;
+            var tableName = entityDescriptor.tableName;
+
+
+            var sql = $"WITH tmp AS ( {NewLine}";
+            sql += sqlInner;
+
+            sql += $"{NewLine}){NewLine}";
+            sql += $"delete from {sqlTranslator.DelimitIdentifier(tableName)} ";
+
+            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
+
+            return sql;
+        }
+
+
+        public ExecuteDeleteTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            // primary key
+            return $"{prefix} {sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}";
+        }
+
+
+
+    }
+}

+ 84 - 0
src/Vit.Orm.Mysql/Vit.Orm.Mysql/TranslateService/ExecuteUpdateTranslateService.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.Mysql.TranslateService
+{
+    public class ExecuteUpdateTranslateService : BaseQueryTranslateService
+    {
+        /*
+
+-- multiple
+WITH tmp AS (
+    select  ('u' + cast(u.id as varchar(max)) + '_' + COALESCE(cast(father.id as varchar(max)),'') ) as name , u.id 
+    from [User] u
+    left join [User] father on u.fatherId = father.id 
+    where u.id > 0
+)
+UPDATE [User]
+  SET name =  tmp.name
+  from [User] t0
+  inner join tmp on t0.id=tmp.id ;
+         */
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var sqlInner = base.BuildQuery(arg, stream);
+
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+            var columnsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate?.memberArgs;
+
+            var NewLine = "\r\n";
+            var keyName = entityDescriptor.keyName;
+            var tableName = entityDescriptor.tableName;
+
+
+            var sql = $"WITH tmp AS ( {NewLine}";
+            sql += sqlInner;
+
+            sql += $"{NewLine}){NewLine}";
+            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)}{NewLine}";
+            sql += $"Set ";
+
+            var sqlToUpdateCols = columnsToUpdate
+                .Select(m => m.name)
+                .Select(name => $"{NewLine}  {sqlTranslator.DelimitIdentifier(name)} = {sqlTranslator.GetSqlField("tmp", name)} ");
+
+            sql += string.Join(",", sqlToUpdateCols);
+
+            sql += $"{NewLine}from {sqlTranslator.DelimitIdentifier(tableName)} t0";
+            sql += $"{NewLine}inner join tmp on t0.{sqlTranslator.DelimitIdentifier(keyName)}=tmp.{sqlTranslator.DelimitIdentifier(keyName)}";
+
+            return sql;
+        }
+ 
+
+        public ExecuteUpdateTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+            var columnsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate?.memberArgs;
+
+            if (columnsToUpdate?.Any() != true) throw new ArgumentException("can not get columns to update");
+
+            var sqlFields = new List<string>();
+
+            foreach (var column in columnsToUpdate)
+            {
+                sqlFields.Add($"({sqlTranslator.EvalExpression(arg, column.value)}) as {sqlTranslator.DelimitIdentifier(column.name)}");
+            }
+            // primary key
+            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}");
+
+            return prefix + " " + String.Join(",", sqlFields);
+        }
+
+
+
+    }
+}

+ 0 - 66
src/Vit.Orm.Mysql/Vit.Orm.Mysql/Translator/ExecuteDeleteTranslator.cs

@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql.Translator;
-
-namespace Vit.Orm.Mysql.Translator
-{
-    public class ExecuteDeleteTranslator : BaseQueryTranslator
-    {
-        /*
-WITH tmp AS (
-    select u.id 
-    from User u
-    left join User father on u.fatherId = father.id 
-    where u.id > 0
-)
-delete from User where id in ( SELECT id FROM tmp );
-         */
-        public override string BuildQuery(CombinedStream stream)
-        {
-            var sqlInner = base.BuildQuery(stream);
-
-
-            var NewLine = "\r\n";
-            var keyName = entityDescriptor.keyName;
-            var tableName = entityDescriptor.tableName;
-
-
-            var sql = $"WITH tmp AS ( {NewLine}";
-            sql += sqlInner;
-
-            sql += $"{NewLine}){NewLine}";
-            sql += $"delete from {sqlTranslator.DelimitIdentifier(tableName)} ";
-
-            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
-
-            return sql;
-        }
-
-
-
-        IEntityDescriptor entityDescriptor;
-
-        public ExecuteDeleteTranslator(SqlTranslator sqlTranslator) : base(sqlTranslator)
-        {
-        }
-
-        protected override string ReadSelect(CombinedStream stream)
-        {
-            var entityType = (stream.source as SourceStream)?.GetEntityType();
-            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-            if (entityDescriptor == null) throw new ArgumentException("Entity can not be deleted");
-
-            var sqlFields = new List<string>();
-
-            // primary key
-            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as `{entityDescriptor.keyName}`");
-            return String.Join(",", sqlFields);
-        }
-
-
-
-    }
-}

+ 6 - 3
src/Vit.Orm.Mysql/Vit.Orm.Mysql/Vit.Orm.Mysql.csproj

@@ -11,12 +11,15 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <!--mysql-->
+      <Compile Remove="SqlTransactionScope.cs" />
+    </ItemGroup>
+
+    <ItemGroup>
         <PackageReference Include="MySql.Data" Version="8.4.0" />
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\..\Vit.Orm\Vit.Orm.csproj" />
+        <ProjectReference Include="..\..\Vit.Orm\Vit.Orm.csproj" />
     </ItemGroup>
- 
+
 </Project>

+ 10 - 12
src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/DataSource.cs

@@ -2,10 +2,9 @@
 
 using Vit.Orm.Sql;
 using Vit.Extensions;
-using Vit.Core.Module.Serialization;
 using Vit.Core.Util.ConfigurationManager;
 
-namespace Vit.Orm.SqlServer.MsTest
+namespace Vit.Orm.MsTest
 {
     [Table("User")]
     public class User
@@ -24,20 +23,19 @@ namespace Vit.Orm.SqlServer.MsTest
     public class DataSource
     {
         static string connectionString = Appsettings.json.GetStringByPath("App.Db.ConnectionString");
-        public static DbContext CreateDbContext(string dbName = "DataSource")
-        { 
-            var dbContext = new SqlDbContext();
-            dbContext.UseMysql(connectionString);
-            return dbContext; 
-        }
- 
-        public static DbContext CreateFormatedDbContext(string dbName = "DataSource")
+
+        public static SqlDbContext CreateDbContext()
         {
             var dbContext = new SqlDbContext();
-            dbContext.UseMysql(connectionString);
+            dbContext.UseSqlServer(connectionString);
+
+            dbContext.BeginTransaction();
 
             var userSet = dbContext.DbSet<User>();
-            //userSet.Create();
+
+            dbContext.Execute(sql: "IF OBJECT_ID(N'User', N'U') IS  NOT  NULL \r\nDROP TABLE [User];");
+
+            userSet.Create();
 
             var users = new List<User> {
                     new User {   name="u1", fatherId=4, motherId=6 },

+ 48 - 0
src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/DbFunction_Test.cs

@@ -0,0 +1,48 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+
+namespace Vit.Orm.MsTest
+{
+
+    [TestClass]
+    public class DbFunction_Test
+    {
+        [TestMethod]
+        public void Test_DbFunction()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+
+            // select * from `User` as t0  where IIF(`t0`.`fatherId` is not null,true, false)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<int>("IIF", u.fatherId != null, 1, 0)==1);
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(3, userList.Last().id);
+            }
+
+            {
+                var query = userQuery.Where(u => u.birth < DbFunction.Call<DateTime>("GETDATE"));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(6, userList.Count);
+            }
+
+            // coalesce(parameter1,parameter2, …)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(1, userList.First().id);
+            }
+
+
+        }
+
+
+    }
+}

+ 5 - 0
src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/Vit.Orm.SqlServer.MsTest.csproj

@@ -7,6 +7,7 @@
 
         <IsPackable>false</IsPackable>
         <IsTestProject>true</IsTestProject>
+        <RootNamespace>Vit.Orm.MsTest</RootNamespace>
     </PropertyGroup>
 
     <ItemGroup>
@@ -27,6 +28,10 @@
         </None>
     </ItemGroup>
 
+    <ItemGroup>
+        <Compile Include="..\..\..\Vit.Orm.Sqlite\Test\Vit.Orm.Sqlite.MsTest\CommonTest\*.cs" Link="CommonTest\%(RecursiveDir)%(FileName)%(Extension)" />
+        <!--<Compile Include="..\Vit.Linq.MsTest\DataSource.cs" />--> 
+    </ItemGroup>
 
 
 </Project>

+ 5 - 3
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/DbContext_Extensions.cs

@@ -4,22 +4,24 @@ using System.Data;
 using Vit.Orm.Entity;
 using Vit.Orm.Entity.Dapper;
 using Vit.Orm.Sql;
+using Vit.Orm.Sql.SqlTranslate;
 using Vit.Orm.SqlServer;
 
 namespace Vit.Extensions
 {
     public static class DbContext_Extensions
     {
-        public static SqlDbContext UseMysql(this SqlDbContext dbContext, string ConnectionString)
+        public static SqlDbContext UseSqlServer(this SqlDbContext dbContext, string ConnectionString)
         {
-            ISqlTranslator sqlTranslator = new SqlTranslator(dbContext);
+            ISqlTranslateService sqlTranslateService = Vit.Orm.SqlServer.SqlTranslateService.Instance;
 
             Func<IDbConnection> createDbConnection = () => new Microsoft.Data.SqlClient.SqlConnection(ConnectionString);
 
             Func<Type, IEntityDescriptor> getEntityDescriptor = (type) => EntityDescriptor.GetEntityDescriptor(type);
 
 
-            dbContext.Init(sqlTranslator: sqlTranslator, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
+            dbContext.Init(sqlTranslateService: sqlTranslateService, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
+            dbContext.createTransactionScope = (dbContext) => new Vit.Orm.SqlServer.SqlTransactionScope(dbContext);
 
             return dbContext;
         }

+ 77 - 0
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTransactionScope.cs

@@ -0,0 +1,77 @@
+using System;
+using System.Data;
+
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.Transaction;
+
+using SqlTransaction = Microsoft.Data.SqlClient.SqlTransaction;
+
+namespace Vit.Orm.SqlServer
+{
+    public class SqlTransactionScope : Vit.Orm.Sql.Transaction.SqlTransactionScope
+    {
+        int savePointCount = 0;
+        public DbTransactionWrap CreateTransactionSavePoint(IDbTransaction originalTransaction)
+        {
+            var savePointName = "tran" + savePointCount++;
+            return new DbTransactionWrapSavePoint(originalTransaction, savePointName);
+        }
+        public SqlTransactionScope(SqlDbContext dbContext) : base(dbContext)
+        {
+        }
+
+        public override IDbTransaction BeginTransaction()
+        {
+            DbTransactionWrap transactionWrap;
+            IDbTransaction originalTransaction = GetCurrentTransaction();
+            if (originalTransaction == null)
+            {
+                var dbConnection = dbContext.dbConnection;
+                if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
+                originalTransaction = dbConnection.BeginTransaction();
+
+                transactionWrap = new DbTransactionWrap(originalTransaction);
+            }
+            else
+            {
+                transactionWrap = CreateTransactionSavePoint(originalTransaction);
+            }
+
+            transactions.Push(transactionWrap);
+            return transactionWrap;
+        }
+
+    }
+
+    public class DbTransactionWrapSavePoint : DbTransactionWrap
+    {
+        public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
+        string savePoint;
+        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePoint) : base(transaction)
+        {
+            this.savePoint = savePoint;
+            sqlTran.Save(savePoint);
+        }
+
+        public override void Commit()
+        {
+            // no need to commit savepoint for sqlserver, ref: https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqltransaction.save
+
+            //sqlTran.Commit(savePoint);
+            TransactionState = ETransactionState.Committed;
+        }
+
+        public override void Dispose()
+        {
+            if (TransactionState == ETransactionState.Active)
+                sqlTran.Rollback(savePoint);
+            TransactionState = ETransactionState.Disposed;
+        }
+
+        public override void Rollback()
+        {
+            sqlTran.Rollback(savePoint);
+            TransactionState = ETransactionState.RolledBack;
+        }
+    }
+}

+ 175 - 0
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/BaseQueryTranslateService.cs

@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.SqlServer.SqlTranslate
+{
+
+    public abstract class BaseQueryTranslateService : Vit.Orm.Sql.SqlTranslate.BaseQueryTranslateService
+    {
+        public BaseQueryTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+
+        /*
+SELECT [t].[id], [t].[birth], [t].[fatherId], [t].[motherId], [t].[name]
+FROM (
+    SELECT [m].[id], [m].[birth], [m].[fatherId], [m].[motherId], [m].[name], ROW_NUMBER() OVER(ORDER BY [m].[fatherId], [m].[motherId] DESC) AS [__RowNumber__]
+    FROM [User] AS [m]
+    WHERE [m].[id] <> 2
+) AS [t]
+WHERE ([t].[__RowNumber__] > 1) AND ([t].[__RowNumber__] <= 13);
+
+// if no orders:
+ROW_NUMBER() OVER(ORDER BY @@RowCount) AS [__RowNumber__]
+
+         */
+
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            if (stream.skip > 0)
+            {
+                #region ROW_NUMBER() OVER(ORDER BY [m].[fatherId], [m].[motherId] DESC) AS [__RowNumber__]
+
+                string sql = "";
+
+
+                #region #0 select
+                sql += ReadSelect(arg, stream);
+
+                // , ROW_NUMBER() OVER(ORDER BY [m].[fatherId], [m].[motherId] DESC) AS [__RowNumber__]
+                if (stream.orders?.Any() == true)
+                {
+                    var orderBy = ReadOrderBy(arg, stream);
+                    sql += $", ROW_NUMBER() OVER(ORDER BY {orderBy}) AS [__RowNumber__]";
+                }
+                else
+                {
+                    sql += $", ROW_NUMBER() OVER(ORDER BY @@RowCount) AS [__RowNumber__]";
+                }
+                #endregion
+
+
+                #region #1 from
+                sql += "\r\n from " + ReadInnerTable(arg, stream.source);
+                #endregion
+
+                #region #2 join
+                if (stream.joins != null)
+                {
+                    sql += ReadJoin(arg, stream);
+                }
+                #endregion
+
+                // #3 where 1=1
+                if (stream.where != null)
+                {
+                    var where = sqlTranslator.EvalExpression(arg, stream.where);
+                    if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n where " + where;
+                }
+
+                #region #4 group by
+                if (stream.groupByFields != null)
+                {
+                    sql += "\r\n group by " + ReadGroupBy(arg, stream);
+                }
+                #endregion
+
+                #region #5 having
+                if (stream.having != null)
+                {
+                    var where = sqlTranslator.EvalExpression(arg, stream.having);
+                    if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n having " + where;
+                }
+                #endregion
+
+
+                #region #6 Range
+                /*
+SELECT *
+FROM (
+    SELECT *, ROW_NUMBER() OVER(ORDER BY @@RowCount) AS [__RowNumber__]
+    FROM [User] AS [m]
+    WHERE [m].[id] <> 2
+) AS [t]
+WHERE ([t].[__RowNumber__] > 1) AND ([t].[__RowNumber__] <= 13);
+                 */
+                return $@"
+SELECT *
+FROM (
+    {sql}
+) AS [t]
+WHERE [t].[__RowNumber__] > {stream.skip} {(stream.take > 0 ? "AND [t].[__RowNumber__] <= " + (stream.take + stream.skip) : "")} ;
+";
+
+                #endregion
+
+
+                #endregion
+
+            }
+            else
+            {
+                #region select top 10 *
+
+                string sql = "select ";
+
+                // #0  select
+                if (stream.take != null) sql += "top " + stream.take + " ";
+                sql += ReadSelect(arg, stream, prefix: null);
+
+
+                #region #1 from
+                sql += "\r\n from " + ReadInnerTable(arg, stream.source);
+                #endregion
+
+                #region #2 join
+                if (stream.joins != null)
+                {
+                    sql += ReadJoin(arg, stream);
+                }
+                #endregion
+
+                // #3 where 1=1
+                if (stream.where != null)
+                {
+                    var where = sqlTranslator.EvalExpression(arg, stream.where);
+                    if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n where " + where;
+                }
+
+                #region #4 group by
+                if (stream.groupByFields != null)
+                {
+                    sql += "\r\n group by " + ReadGroupBy(arg, stream);
+                }
+                #endregion
+
+                #region #5 having
+                if (stream.having != null)
+                {
+                    var where = sqlTranslator.EvalExpression(arg, stream.having);
+                    if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n having " + where;
+                }
+                #endregion
+
+
+                // #6 OrderBy
+                if (stream.orders?.Any() == true)
+                {
+                    sql += "\r\n order by " + ReadOrderBy(arg, stream);
+                }
+
+                return sql;
+                #endregion
+            }
+        }
+
+
+
+    }
+
+}

+ 59 - 0
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/ExecuteDeleteTranslateService.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.SqlServer.TranslateService
+{
+    public class ExecuteDeleteTranslateService : Vit.Orm.SqlServer.SqlTranslate.BaseQueryTranslateService
+    {
+        /*
+WITH tmp AS (
+    select u.id 
+    from User u
+    left join User father on u.fatherId = father.id 
+    where u.id > 0
+)
+delete from User where id in ( SELECT id FROM tmp );
+         */
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            var sqlInner = base.BuildQuery(arg, stream);
+
+
+            var NewLine = "\r\n";
+            var keyName = entityDescriptor.keyName;
+            var tableName = entityDescriptor.tableName;
+
+
+            var sql = $"WITH tmp AS ( {NewLine}";
+            sql += sqlInner;
+
+            sql += $"{NewLine}){NewLine}";
+            sql += $"delete from {sqlTranslator.DelimitIdentifier(tableName)} ";
+
+            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
+
+            return sql;
+        }
+
+
+        public ExecuteDeleteTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            // primary key
+            return $"{prefix} {sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}";
+        }
+
+
+
+    }
+}

+ 85 - 0
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/ExecuteUpdateTranslateService.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.SqlServer.TranslateService
+{
+    public class ExecuteUpdateTranslateService : Vit.Orm.SqlServer.SqlTranslate.BaseQueryTranslateService
+    {
+        /*
+
+-- multiple
+WITH tmp AS (
+    select  ('u' + cast(u.id as varchar(max)) + '_' + COALESCE(cast(father.id as varchar(max)),'') ) as name , u.id 
+    from [User] u
+    left join [User] father on u.fatherId = father.id 
+    where u.id > 0
+)
+UPDATE [User]
+  SET name =  tmp.name
+  from [User] t0
+  inner join tmp on t0.id=tmp.id ;
+
+         */
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var sqlInner = base.BuildQuery(arg, stream);
+
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+            var columnsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate?.memberArgs;
+
+            var NewLine = "\r\n";
+            var keyName = entityDescriptor.keyName;
+            var tableName = entityDescriptor.tableName;
+
+
+            var sql = $"WITH tmp AS ( {NewLine}";
+            sql += sqlInner;
+
+            sql += $"{NewLine}){NewLine}";
+            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)}{NewLine}";
+            sql += $"Set ";
+
+            var sqlToUpdateCols = columnsToUpdate
+                .Select(m => m.name)
+                .Select(name => $"{NewLine}  {sqlTranslator.DelimitIdentifier(name)} = {sqlTranslator.GetSqlField("tmp", name)} ");
+
+            sql += string.Join(",", sqlToUpdateCols);
+
+            sql += $"{NewLine}from {sqlTranslator.DelimitIdentifier(tableName)} t0";
+            sql += $"{NewLine}inner join tmp on t0.{sqlTranslator.DelimitIdentifier(keyName)}=tmp.{sqlTranslator.DelimitIdentifier(keyName)}";
+
+            return sql;
+        }
+
+
+        public ExecuteUpdateTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+            var columnsToUpdate = (stream as StreamToUpdate) ?.fieldsToUpdate?.memberArgs;
+
+            if (columnsToUpdate?.Any() != true) throw new ArgumentException("can not get columns to update");
+
+            var sqlFields = new List<string>();
+
+            foreach (var column in columnsToUpdate)
+            {
+                sqlFields.Add($"({sqlTranslator.EvalExpression(arg, column.value)}) as {sqlTranslator.DelimitIdentifier(column.name)}");
+            }
+            // primary key
+            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}");
+
+            return prefix + " " + String.Join(",", sqlFields);
+        }
+
+
+
+    }
+}

+ 66 - 0
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslate/QueryTranslateService.cs

@@ -0,0 +1,66 @@
+using System;
+
+using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.DataReader;
+using Vit.Orm.Sql.DataReader;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.SqlServer.SqlTranslate
+{
+    public class QueryTranslateService: BaseQueryTranslateService
+    {
+
+        /* // sql
+SELECT [t].[id], [t].[birth], [t].[fatherId], [t].[motherId], [t].[name]
+FROM (
+    SELECT [m].[id], [m].[birth], [m].[fatherId], [m].[motherId], [m].[name], ROW_NUMBER() OVER(ORDER BY [m].[fatherId], [m].[motherId] DESC) AS [__RowNumber__]
+    FROM [User] AS [m]
+    WHERE [m].[id] <> 2
+) AS [t]
+WHERE ([t].[__RowNumber__] > 1) AND ([t].[__RowNumber__] <= 13);
+
+// if no orders:
+ROW_NUMBER() OVER(ORDER BY @@RowCount) AS [__RowNumber__]
+
+         */
+
+        public QueryTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            switch (stream.method)
+            {
+                case "Count":
+                    {
+                        var reader = new NumScalarReader();
+                        if (arg.dataReader == null) arg.dataReader = reader;
+                        return prefix+" "+ "count(*)";
+                    }
+                case "" or null or "ToList" or nameof(Queryable_Extensions.ToExecuteString):
+                    {
+                        var reader = new EntityReader();
+                        return prefix + " " + BuildReader(arg, stream, reader);
+                    }
+                case "FirstOrDefault" or "First" or "LastOrDefault" or "Last":
+                    {
+                        stream.take = 1;
+                        stream.skip = null;
+
+                        if (stream.method.Contains("Last"))
+                            ReverseOrder(arg, stream);
+
+                        var nullable = stream.method.Contains("OrDefault");
+                        var reader = new FirstEntityReader { nullable = nullable };
+                        return prefix + " " + BuildReader(arg, stream, reader);
+                    }
+            }
+            throw new NotSupportedException("not supported method: " + stream.method);
+        }
+
+
+    }
+}

+ 245 - 0
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslateService.cs

@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.Entity;
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.SqlTranslate;
+using Vit.Orm.SqlServer.TranslateService;
+
+
+namespace Vit.Orm.SqlServer
+{
+    public class SqlTranslateService : Vit.Orm.Sql.SqlTranslate.SqlTranslateService
+    {
+        public static readonly SqlTranslateService Instance = new SqlTranslateService();
+
+        protected Vit.Orm.SqlServer.SqlTranslate.QueryTranslateService queryTranslateService;
+        protected ExecuteUpdateTranslateService executeUpdateTranslateService;
+        protected ExecuteDeleteTranslateService executeDeleteTranslateService;
+
+    
+        public SqlTranslateService()
+        {
+            queryTranslateService = new(this);
+            executeUpdateTranslateService = new ExecuteUpdateTranslateService(this);
+            executeDeleteTranslateService = new ExecuteDeleteTranslateService(this);
+        }
+
+        /// <summary>
+        ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to delimit.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public override string DelimitIdentifier(string identifier) => $"[{EscapeIdentifier(identifier)}]"; // Interpolation okay; strings
+
+        /// <summary>
+        ///     Generates the escaped SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to be escaped.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public override string EscapeIdentifier(string identifier) => identifier.Replace("[", "\"[").Replace("]", "\"]");
+
+
+        #region EvalExpression
+        /// <summary>
+        /// read where or value or on
+        /// </summary>
+        /// <param name="arg"></param>
+        /// <returns></returns>
+        /// <exception cref="NotSupportedException"></exception>
+        /// <param name="data"></param>
+        public override string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
+        {
+            switch (data.nodeType)
+            {
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall methodCall = data;
+                        switch (methodCall.methodName)
+                        {
+                            // ##1 ToString
+                            case nameof(object.ToString):
+                                {
+                                    return $"cast({EvalExpression(arg, methodCall.@object)} as varchar(max))";
+                                }
+
+                            #region ##2 String method:  StartsWith EndsWith Contains
+                            case nameof(string.StartsWith): // String.StartsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like {EvalExpression(arg, value)}+'%'";
+                                }
+                            case nameof(string.EndsWith): // String.EndsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}";
+                                }
+                            case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}+'%'";
+                                }
+                            #endregion
+                        }
+                        break;
+                    }
+
+                #region Read Value
+                case NodeType.Convert:
+                    {
+                        // cast( 4.1 as signed)
+
+                        ExpressionNode_Convert convert = data;
+
+                        Type targetType = convert.valueType?.ToType();
+
+                        if (targetType == typeof(object)) return EvalExpression(arg, convert.body);
+
+                        // Nullable
+                        if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
+
+                        string targetDbType = GetDbType(targetType);
+
+                        var sourceType = convert.body.Member_GetType();
+                        if (sourceType != null)
+                        {
+                            if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
+
+                            if (targetDbType == GetDbType(sourceType)) return EvalExpression(arg, convert.body);
+                        }
+
+                        return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
+                    }
+                case nameof(ExpressionType.Add):
+                    {
+                        ExpressionNode_Binary binary = data;
+
+                        // ##1 String Add
+                        if (data.valueType?.ToType() == typeof(string))
+                        {
+                            return $"CONCAT({EvalExpression(arg, binary.left)} ,{EvalExpression(arg, binary.right)})";
+                        }
+
+                        // ##2 Numberic Add
+                        return $"{EvalExpression(arg, binary.left)} + {EvalExpression(arg, binary.right)}";
+                    }
+                case nameof(ExpressionType.Coalesce):
+                    {
+                        ExpressionNode_Binary binary = data;
+                        return $"COALESCE({EvalExpression(arg, binary.left)},{EvalExpression(arg, binary.right)})";
+                    }
+                    #endregion
+
+            }
+
+            return base.EvalExpression(arg, data);
+        }
+        #endregion
+
+
+        #region PrepareCreate
+        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
+        {
+            /* //sql
+CREATE TABLE user (
+  id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  name varchar(100) DEFAULT NULL,
+  birth date DEFAULT NULL,
+  fatherId int DEFAULT NULL,
+  motherId int DEFAULT NULL
+) ;
+              */
+            List<string> sqlFields = new();
+
+            // #1 primary key
+            sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY IDENTITY(1,1)");
+
+            // #2 columns
+            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+
+            return $@"
+CREATE TABLE [dbo].{DelimitIdentifier(entityDescriptor.tableName)} (
+{string.Join(",\r\n  ", sqlFields)}
+)";
+
+
+            string GetColumnSql(IColumnDescriptor column)
+            {
+                bool nullable = false;
+
+                var type = column.type;
+                if (type.IsGenericType || type == typeof(string))
+                {
+                    nullable = true;
+                    type = TypeUtil.GetUnderlyingType(type);
+                }
+                // name varchar(100) DEFAULT NULL
+                return $"  {DelimitIdentifier(column.name)} {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
+            }
+        }
+
+        protected readonly static Dictionary<Type, string> dbTypeMap = new()
+        {
+            [typeof(DateTime)] = "datetime",
+            [typeof(string)] = "varchar(max)",
+
+            [typeof(float)] = "float",
+            [typeof(double)] = "float",
+            [typeof(decimal)] = "float",
+
+            [typeof(Int32)] = "int",
+            [typeof(Int16)] = "smallint",
+            [typeof(byte)] = "tinyint",
+            [typeof(bool)] = "bit",
+        };
+        protected override string GetDbType(Type type)
+        {
+            if (dbTypeMap.TryGetValue(type, out var dbType)) return dbType;
+            throw new NotSupportedException("unsupported column type:" + type.Name);
+        }
+        #endregion
+
+
+
+        public override (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg)
+        {
+            var result = base.PrepareAdd(arg);
+
+            // get generated id
+            result.sql += "select convert(int,isnull(SCOPE_IDENTITY(),-1));";
+
+            return result;
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = queryTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam, arg.dataReader);
+        }
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeUpdateTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeDeleteTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+
+
+    }
+}

+ 0 - 128
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/SqlTranslator.cs

@@ -1,128 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Extensions.Linq_Extensions;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql;
-using Vit.Orm.SqlServer.Translator;
-
-
-namespace Vit.Orm.SqlServer
-{
-    public class SqlTranslator : Vit.Orm.Sql.Translator.SqlTranslator
-    {
-        public SqlTranslator(DbContext dbContext):base(dbContext) 
-        {
-        }
-
-        /// <summary>
-        ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
-        /// </summary>
-        /// <param name="identifier">The identifier to delimit.</param>
-        /// <returns>
-        ///     The generated string.
-        /// </returns>
-        public override string DelimitIdentifier(string identifier) => $"[{EscapeIdentifier(identifier)}]"; // Interpolation okay; strings
-
-        /// <summary>
-        ///     Generates the escaped SQL representation of an identifier (column name, table name, etc.).
-        /// </summary>
-        /// <param name="identifier">The identifier to be escaped.</param>
-        /// <returns>
-        ///     The generated string.
-        /// </returns>
-        public override string EscapeIdentifier(string identifier) => identifier.Replace("[", "\"[").Replace("]", "\"]");
-
-        #region PrepareCreate
-        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
-        {
-            /* //sql
-CREATE TABLE user (
-  id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  name varchar(100) DEFAULT NULL,
-  birth date DEFAULT NULL,
-  fatherId int DEFAULT NULL,
-  motherId int DEFAULT NULL
-) ;
-              */
-            List<string> sqlFields = new();
-
-            // #1 primary key
-            sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY IDENTITY(1,1)");
-
-            // #2 columns
-            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
-
-            return $@"
-CREATE TABLE [dbo].{DelimitIdentifier(entityDescriptor.tableName)} (
-{string.Join(",\r\n  ", sqlFields)}
-)";
-
-
-            string GetColumnSql(IColumnDescriptor column)
-            {
-                bool nullable = false;
-
-                var type = column.type;
-                if (type.IsGenericType || type == typeof(string))
-                {
-                    nullable = true;
-                    type = TypeUtil.GetUnderlyingType(type);
-                }
-                // name varchar(100) DEFAULT NULL
-                return $"  {DelimitIdentifier(column.name)} {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
-            }
-        }
-
-        protected readonly static Dictionary<Type, string> dbTypeMap = new ()
-        {
-            [typeof(DateTime)] = "datetime",
-            [typeof(string)] = "varchar(1000)",
-
-            [typeof(float)] = "float",
-            [typeof(double)] = "double",
-            [typeof(decimal)] = "decimal",
-            [typeof(Int32)] = "int",
-            [typeof(Int16)] = "smallint",
-            [typeof(byte)] = "tinyint",
-            [typeof(bool)] = "bit",
-        };
-        protected override string GetDbType(Type type)
-        { 
-            if (dbTypeMap.TryGetValue(type,out var dbType))return dbType;
-                return "datetime";
-
-            throw new NotSupportedException("unsupported column type:" + type.Name);
-        }
-        #endregion
-
-
-        public override (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) PrepareAdd<Entity>(DbSet<Entity> dbSet) 
-        {
-            var result=base.PrepareAdd(dbSet);
-
-            result.sql += "select convert(int,isnull(SCOPE_IDENTITY(),-1));";
-
-            return result;
-        }
-
-        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(CombinedStream combinedStream)
-        {
-            var query = new ExecuteUpdateTranslator(this);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam);
-        }
-
-        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(CombinedStream combinedStream)
-        {
-            var query = new ExecuteDeleteTranslator(this);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam);
-        }
-
-
-
-    }
-}

+ 0 - 66
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/Translator/ExecuteDeleteTranslator.cs

@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql.Translator;
-
-namespace Vit.Orm.SqlServer.Translator
-{
-    public class ExecuteDeleteTranslator : BaseQueryTranslator
-    {
-        /*
-WITH tmp AS (
-    select u.id 
-    from User u
-    left join User father on u.fatherId = father.id 
-    where u.id > 0
-)
-delete from User where id in ( SELECT id FROM tmp );
-         */
-        public override string BuildQuery(CombinedStream stream)
-        {
-            var sqlInner = base.BuildQuery(stream);
-
-
-            var NewLine = "\r\n";
-            var keyName = entityDescriptor.keyName;
-            var tableName = entityDescriptor.tableName;
-
-
-            var sql = $"WITH tmp AS ( {NewLine}";
-            sql += sqlInner;
-
-            sql += $"{NewLine}){NewLine}";
-            sql += $"delete from {sqlTranslator.DelimitIdentifier(tableName)} ";
-
-            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
-
-            return sql;
-        }
-
-
-
-        IEntityDescriptor entityDescriptor;
-
-        public ExecuteDeleteTranslator(SqlTranslator sqlTranslator) : base(sqlTranslator)
-        {
-        }
-
-        protected override string ReadSelect(CombinedStream stream)
-        {
-            var entityType = (stream.source as SourceStream)?.GetEntityType();
-            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-            if (entityDescriptor == null) throw new ArgumentException("Entity can not be deleted");
-
-            var sqlFields = new List<string>();
-
-            // primary key
-            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as `{entityDescriptor.keyName}`");
-            return String.Join(",", sqlFields);
-        }
-
-
-
-    }
-}

+ 0 - 91
src/Vit.Orm.SqlServer/Vit.Orm.SqlServer/Translator/ExecuteUpdateTranslator.cs

@@ -1,91 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql.Translator;
-
-namespace Vit.Orm.SqlServer.Translator
-{
-    public class ExecuteUpdateTranslator : BaseQueryTranslator
-    {
-        /*
-
--- multiple
-WITH tmp AS (
-    select   ('u' || u.id || '_' || COALESCE(father.id,'') ) as _name , u.id 
-    from User u
-    left join User father on u.fatherId = father.id 
-    where u.id > 0
-)
-UPDATE User  
-  SET name =  ( SELECT _name FROM tmp WHERE tmp.id =User.id )
-where id in ( SELECT id FROM tmp );
-
-
---- single
-UPDATE User SET name = 'u'||id  where id > 0;
-         */
-        public override string BuildQuery(CombinedStream stream)
-        {
-            var sqlInner = base.BuildQuery(stream);
-
-
-            var NewLine = "\r\n";
-            var keyName = entityDescriptor.keyName;
-            var tableName = entityDescriptor.tableName;
-
-
-            var sql = $"WITH tmp AS ( {NewLine}";
-            sql += sqlInner;
-
-            sql += $"{NewLine}){NewLine}";
-            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)} ";
-
-            var sqlToUpdateCols = columnsToUpdate.Select(m => m.name).Select(name => $"{NewLine}  SET {sqlTranslator.DelimitIdentifier(name)} =  ( SELECT {sqlTranslator.DelimitIdentifier("_" + name)} FROM tmp WHERE tmp.{sqlTranslator.DelimitIdentifier(keyName)} ={sqlTranslator.GetSqlField(tableName, keyName)} )");
-            sql += string.Join(",", sqlToUpdateCols);
-
-            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
-
-            return sql;
-        }
-
-
-        List<MemberBind> columnsToUpdate;
-        IEntityDescriptor entityDescriptor;
-
-        public ExecuteUpdateTranslator(SqlTranslator sqlTranslator) : base(sqlTranslator)
-        {
-        }
-
-        protected override string ReadSelect(CombinedStream stream)
-        {
-            var fieldsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate;
-
-            columnsToUpdate = (fieldsToUpdate?.constructorArgs ?? new()).AsQueryable().Concat(fieldsToUpdate?.memberArgs ?? new()).ToList();
-            if (columnsToUpdate?.Any() != true) throw new ArgumentException("can not get columns to update");
-
-
-            var entityType = fieldsToUpdate.New_GetType();
-            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-            if (entityDescriptor == null) throw new ArgumentException("Entity can not be updated");
-
-
-            var sqlFields = new List<string>();
-
-            foreach (var column in columnsToUpdate)
-            {
-                sqlFields.Add($"({ReadEval(column.value)}) as {sqlTranslator.DelimitIdentifier("_" + column.name)}");
-            }
-
-            // primary key
-            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}");
-            return String.Join(",", sqlFields);
-        }
-
-
-
-    }
-}

+ 0 - 279
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CRUD_Test.cs

@@ -1,279 +0,0 @@
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-
-    [TestClass]
-    public class CRUD_Test
-    {
-        #region #1 Create
-
-        [TestMethod]
-        public void Test_Create()
-        {
-            var user = new User { id = 7, name = "testUser7", birth = DateTime.Now, fatherId = 1, motherId = 2 };
-            var user2 = new User { id = 8, name = "testUser8", birth = DateTime.Now, fatherId = 3, motherId = 4 };
-
-            // #1 Add
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                dbContext.Add(user);
-
-                Assert.AreEqual(7, userQuery.Count());
-
-                var newUser = userQuery.FirstOrDefault(m => m.id == 7);
-                Assert.AreEqual(user.id, newUser?.id);
-                Assert.AreEqual(user.name, newUser?.name);
-            }
-
-            // #2 AddRange
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                dbContext.AddRange(new[] { user, user2 });
-
-                Assert.AreEqual(8, userQuery.Count());
-
-                var newUsers = userQuery.Where(m => m.id >= 7).ToList();
-                Assert.AreEqual(2, newUsers.Count());
-                Assert.AreEqual(user.id, newUsers[0]?.id);
-                Assert.AreEqual(user2.id, newUsers[1]?.id);
-            }
-
-        }
-        #endregion
-
-
-        #region #3 Update
-
-        [TestMethod]
-        public void Test_Update()
-        {
-            var user = new User { id = 4, name = "testUser4", birth = DateTime.Now, fatherId = 14 };
-            var user2 = new User { id = 5, name = "testUser5", birth = DateTime.Now, fatherId = 15 };
-
-            #region Update
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = dbContext.Update(user);
-                Assert.AreEqual(1, rowCount);
-
-                var newUser = userQuery.FirstOrDefault(m => m.id == 4);
-                Assert.AreEqual(4, newUser.id);
-                Assert.AreEqual(user.name, newUser.name);
-                Assert.AreEqual(user.birth, newUser.birth);
-                Assert.AreEqual(user.fatherId, newUser.fatherId);
-            }
-            #endregion
-
-            #region UpdateRange
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = dbContext.UpdateRange(new[] { user, user2 });
-                Assert.AreEqual(2, rowCount);
-
-                var newUsers = userQuery.Where(m => m.id == 4 || m.id == 5).ToList();
-                Assert.AreEqual(user.id, newUsers[0].id);
-                Assert.AreEqual(user.name, newUsers[0].name);
-                Assert.AreEqual(user2.id, newUsers[1].id);
-                Assert.AreEqual(user2.name, newUsers[1].name);
-            }
-            #endregion
-
-        }
-
-
-        [TestMethod]
-        public void Test_ExecuteUpdate()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var count = userQuery.ExecuteUpdate(row => new User
-                {
-                    name = "u_" + row.id + "_" + (row.fatherId.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-
-                Assert.AreEqual(6, count);
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u_1_4_6", userList.First().name);
-                Assert.AreEqual("u_6__", userList.Last().name);
-            }
-
-
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             select new
-                             {
-                                 user,
-                                 father,
-                                 motherId = user.motherId
-                             });
-
-                var count = query.ExecuteUpdate(row => new User
-                {
-                    name = "u2_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-                Assert.AreEqual(6, count);
-
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u2_1_4_6", userList.First().name);
-                Assert.AreEqual("u2_6__", userList.Last().name);
-            }
-
-
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id <= 5 && father != null
-                             select new
-                             {
-                                 user,
-                                 father,
-                                 motherId = user.motherId
-                             });
-
-                var count = query.ExecuteUpdate(row => new User
-                {
-                    name = "u3_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
-                });
-                Assert.AreEqual(3, count);
-
-
-                var userList = userQuery.ToList();
-                Assert.AreEqual("u3_1_4_6", userList[0].name);
-                Assert.AreEqual("u3_3_5_6", userList[2].name);
-                Assert.AreEqual("u2_4__", userList[3].name);
-            }
-        }
-        #endregion
-
-
-        #region #4 Delete
-
-
-        [TestMethod]
-        public void Test_Delete()
-        {
-
-            #region #1 Delete
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = dbContext.Delete(new User { id = 5 });
-
-                Assert.AreEqual(1, rowCount);
-                Assert.AreEqual(5, userQuery.Count());
-            }
-            #endregion
-
-            #region #2 DeleteRange
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = dbContext.DeleteRange(new[] { new User { id = 5 }, new User { id = 6 }, new User { id = 10 } });
-
-                Assert.AreEqual(2, rowCount);
-                Assert.AreEqual(4, userQuery.Count());
-            }
-            #endregion
-
-            #region #3 DeleteByKey
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = dbContext.DeleteByKey<User>(4);
-                Assert.AreEqual(1, rowCount);
-                Assert.AreEqual(5, userQuery.Count());
-            }
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = dbContext.DeleteByKey<User>(7);
-                Assert.AreEqual(0, rowCount);
-                Assert.AreEqual(6, userQuery.Count());
-            }
-            #endregion
-
-
-            #region #4 DeleteByKeys
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userSet = dbContext.DbSet<User>();
-
-                var rowCount = userSet.DeleteByKeys(new[] { 5, 6, 10 });
-
-                Assert.AreEqual(2, rowCount);
-                Assert.AreEqual(4, userSet.Query().Count());
-            }
-            #endregion
-        }
-
-
-
-        [TestMethod]
-        public void Test_ExecuteDelete()
-        {
-            if (1 == 1)
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id <= 5 && father != null
-                             select new
-                             {
-                                 user,
-                                 father
-                             });
-
-                var rowCount = query.ExecuteDelete();
-
-                Assert.AreEqual(3, rowCount);
-
-                var newUsers = userQuery.ToList();
-                Assert.AreEqual(3, newUsers.Count());
-                Assert.AreEqual(4, newUsers.First().id);
-                Assert.AreEqual(6, newUsers.Last().id);
-            }
-
-            if (1 == 1)
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var userQuery = dbContext.Query<User>();
-
-                var rowCount = userQuery.Where(m => m.id == 2 || m.id == 4).ExecuteDelete();
-
-                Assert.AreEqual(2, rowCount);
-
-                var newUsers = userQuery.ToList();
-                Assert.AreEqual(4, newUsers.Count());
-                Assert.AreEqual(1, newUsers.First().id);
-                Assert.AreEqual(3, newUsers[1].id);
-                Assert.AreEqual(5, newUsers[2].id);
-            }
-        }
-        #endregion
-
-
-    }
-}

+ 44 - 42
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/CRUD_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/CRUD_Test.cs

@@ -1,9 +1,8 @@
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
 
     [TestClass]
@@ -19,7 +18,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             // #1 Add
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 dbContext.Add(user);
@@ -33,7 +32,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             // #2 AddRange
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 dbContext.AddRange(new[] { user, user2 });
@@ -55,12 +54,13 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_Update()
         {
-            var user = new User { id = 4, name = "testUser4", birth = DateTime.Now, fatherId = 14 };
+            var birth = DateTime.Parse("2021-03-01 00:00:00");
+            var user = new User { id = 4, name = "testUser4", birth = birth, fatherId = 14 };
             var user2 = new User { id = 5, name = "testUser5", birth = DateTime.Now, fatherId = 15 };
 
             #region Update
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = dbContext.Update(user);
@@ -76,7 +76,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             #region UpdateRange
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = dbContext.UpdateRange(new[] { user, user2 });
@@ -96,32 +96,34 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_ExecuteUpdate()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
                 var count = userQuery.ExecuteUpdate(row => new User
                 {
-                    name = "u_" + row.id + "_" + (row.fatherId.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
+                    name = "u_" + row.id + "_" + (row.fatherId.ToString() ?? "") + "_" + (row.motherId.ToString() ?? ""),
+                    birth = DateTime.Parse("2021-01-11 00:00:00")
                 });
 
                 Assert.AreEqual(6, count);
 
                 var userList = userQuery.ToList();
                 Assert.AreEqual("u_1_4_6", userList.First().name);
+                Assert.AreEqual(DateTime.Parse("2021-01-11 00:00:00"), userList.First().birth);
                 Assert.AreEqual("u_6__", userList.Last().name);
             }
 
 
             {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             select new
-                             {
-                                 user,
-                                 father,
-                                 motherId = user.motherId
-                             });
+                var query = from user in userQuery
+                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                            select new
+                            {
+                                user,
+                                father,
+                                user.motherId
+                            };
 
                 var count = query.ExecuteUpdate(row => new User
                 {
@@ -137,15 +139,15 @@ namespace Vit.Orm.Sqlite.MsTest
 
 
             {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id <= 5 && father != null
-                             select new
-                             {
-                                 user,
-                                 father,
-                                 motherId = user.motherId
-                             });
+                var query = from user in userQuery
+                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                            where user.id <= 5 && father != null
+                            select new
+                            {
+                                user,
+                                father,
+                                user.motherId
+                            };
 
                 var count = query.ExecuteUpdate(row => new User
                 {
@@ -172,7 +174,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             #region #1 Delete
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = dbContext.Delete(new User { id = 5 });
@@ -184,7 +186,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             #region #2 DeleteRange
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = dbContext.DeleteRange(new[] { new User { id = 5 }, new User { id = 6 }, new User { id = 10 } });
@@ -196,7 +198,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             #region #3 DeleteByKey
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = dbContext.DeleteByKey<User>(4);
@@ -204,7 +206,7 @@ namespace Vit.Orm.Sqlite.MsTest
                 Assert.AreEqual(5, userQuery.Count());
             }
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = dbContext.DeleteByKey<User>(7);
@@ -216,7 +218,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             #region #4 DeleteByKeys
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userSet = dbContext.DbSet<User>();
 
                 var rowCount = userSet.DeleteByKeys(new[] { 5, 6, 10 });
@@ -234,17 +236,17 @@ namespace Vit.Orm.Sqlite.MsTest
         {
             if (1 == 1)
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id <= 5 && father != null
-                             select new
-                             {
-                                 user,
-                                 father
-                             });
+                var query = from user in userQuery
+                            from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                            where user.id <= 5 && father != null
+                            select new
+                            {
+                                user,
+                                father
+                            };
 
                 var rowCount = query.ExecuteDelete();
 
@@ -258,7 +260,7 @@ namespace Vit.Orm.Sqlite.MsTest
 
             if (1 == 1)
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var userQuery = dbContext.Query<User>();
 
                 var rowCount = userQuery.Where(m => m.id == 2 || m.id == 4).ExecuteDelete();

+ 16 - 11
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_Group_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_Group_Test.cs

@@ -3,7 +3,7 @@ using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
     [TestClass]
     public class Query_Group_Test
@@ -12,7 +12,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_Group_Demo()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -20,8 +20,8 @@ namespace Vit.Orm.Sqlite.MsTest
                 var query =
                         from user in userQuery
                         group user by new { user.fatherId, user.motherId } into userGroup
-                        select new { userGroup.Key.fatherId, userGroup.Key.motherId  };
-            
+                        select new { userGroup.Key.fatherId, userGroup.Key.motherId };
+
                 var sql = query.ToExecuteString();
                 var rows = query.ToList();
 
@@ -59,7 +59,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_Group_Complex()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -67,8 +67,8 @@ namespace Vit.Orm.Sqlite.MsTest
                 var query =
                         from user in userQuery.Where(u => u.id > 1)
                         group user by new { user.fatherId, user.motherId } into userGroup
-                        where userGroup.Key.motherId != null
-                        orderby userGroup.Key.fatherId descending
+                        where userGroup.Key.motherId != null && userGroup.Count() >= 1
+                        orderby userGroup.Key.fatherId descending, userGroup.Count() descending
                         select new { userGroup.Key.fatherId, userGroup.Key.motherId, rowCount = userGroup.Count(), maxId = userGroup.Max(m => m.id) };
 
                 query = query.Skip(1).Take(1);
@@ -119,13 +119,14 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_Others()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
                 var query =
                     userQuery
                     .GroupBy(user => new { user.fatherId, user.motherId })
+                    .OrderByDescending(group => group.Count())
                     .Select(userGroup => new
                     {
                         userGroup.Key.fatherId,
@@ -133,7 +134,7 @@ namespace Vit.Orm.Sqlite.MsTest
                         maxId = userGroup.Max(m => m.id),
                         minId = userGroup.Min(m => m.id),
                         sumId = userGroup.Sum(m => m.id),
-                        avgId = userGroup.Average(m => m.id)
+                        avgId = userGroup.Average(m => (double)m.id)
                     })
                     ;
 
@@ -141,8 +142,12 @@ namespace Vit.Orm.Sqlite.MsTest
                 var rows = query.ToList();
 
                 Assert.AreEqual(3, rows.Count);
-                Assert.AreEqual(2, rows[1].rowCount);
-                Assert.AreEqual(1.5, rows[1].avgId);
+                var row = rows[1];
+                Assert.AreEqual(2, row.rowCount);
+                Assert.AreEqual(2, row.maxId);
+                Assert.AreEqual(1, row.minId);
+                Assert.AreEqual(3, row.sumId);
+                Assert.AreEqual(1.5, row.avgId);
             }
             {
                 var query =

+ 4 - 4
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_InnerJoin_ByJoin_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_InnerJoin_ByJoin_Test.cs

@@ -3,7 +3,7 @@ using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
     [TestClass]
     public class Query_InnerJoin_ByJoin_Test
@@ -12,7 +12,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_InnerJoin_Demo()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -55,7 +55,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_InnerJoin_Complex()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -137,7 +137,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_InnerJoin_Others()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // simple

+ 4 - 4
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_InnerJoin_BySelectMany_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_InnerJoin_BySelectMany_Test.cs

@@ -3,7 +3,7 @@ using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
     [TestClass]
     public class Query_InnerJoin_BySelectMany_Test
@@ -12,7 +12,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_InnerJoin_Demo()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -54,7 +54,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_InnerJoin_Complex()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -128,7 +128,7 @@ namespace Vit.Orm.Sqlite.MsTest
         }
 
 
- 
+
 
 
     }

+ 3 - 3
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_LeftJoin_ByGroupJoin_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_LeftJoin_ByGroupJoin_Test.cs

@@ -3,7 +3,7 @@ using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
     [TestClass]
     public class Query_LeftJoin_ByGroupJoin_Test
@@ -11,7 +11,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_LeftJoin_Demo()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -64,7 +64,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_LeftJoin_Complex()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion

+ 12 - 12
src/Vit.Orm.Mysql/Test/Vit.Orm.Mysql.MsTest/Query_LeftJoin_BySelectMany_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_LeftJoin_BySelectMany_Test.cs

@@ -2,7 +2,7 @@
 using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
     [TestClass]
     public class Query_LeftJoin_BySelectMany_Test
@@ -14,7 +14,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_LeftJoin_Demo()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -61,7 +61,7 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_LeftJoin_Complex()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             // Linq Expresssion
@@ -101,19 +101,19 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_MultipleSelect()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
 
             {
-                var query = (from user in userQuery
+                var query = from user in userQuery
                              from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
                              where user.id > 2 && father != null
                              select new
                              {
                                  user,
                                  father
-                             });
+                             };
 
                 var userList = query.ToList();
 
@@ -122,7 +122,7 @@ namespace Vit.Orm.Sqlite.MsTest
             }
 
             {
-                var query = (from user in userQuery
+                var query = from user in userQuery
                              from father in userQuery.Where(father => user.fatherId == father.id)
                              from mother in userQuery.Where(mother => user.motherId == mother.id)
                              select new
@@ -136,7 +136,7 @@ namespace Vit.Orm.Sqlite.MsTest
                                  hasFather = user.fatherId != null ? true : false,
                                  fatherName = father.name,
                                  mother
-                             });
+                             };
 
                 var userList = query.ToList();
                 Assert.AreEqual(3, userList.Count);
@@ -146,7 +146,7 @@ namespace Vit.Orm.Sqlite.MsTest
             }
 
             {
-                var query = (from user in userQuery
+                var query = from user in userQuery
                              from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
                              from mother in userQuery.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
                              select new
@@ -158,7 +158,7 @@ namespace Vit.Orm.Sqlite.MsTest
                                  hasFather2 = father != null,
                                  fatherName = father.name,
                                  motherName = mother.name,
-                             });
+                             };
 
                 var userList = query.ToList();
 
@@ -170,7 +170,7 @@ namespace Vit.Orm.Sqlite.MsTest
             }
 
             {
-                var query = (from user in userQuery
+                var query = from user in userQuery
                              from father in userQuery.Where(father => user.fatherId == father.id)
                              from mother in userQuery.Where(mother => user.motherId == mother.id)
                              where user.id > 1
@@ -184,7 +184,7 @@ namespace Vit.Orm.Sqlite.MsTest
                                  hasFather2 = father != null,
                                  fatherName = father.name,
                                  motherName = mother.name,
-                             });
+                             };
 
                 var userList = query.ToList();
 

+ 32 - 71
src/Vit.Orm.SqlServer/Test/Vit.Orm.SqlServer.MsTest/Query_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Query_Test.cs

@@ -1,9 +1,8 @@
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Vit.Extensions.Linq_Extensions;
 using System.Data;
 
-namespace Vit.Orm.SqlServer.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
 
     [TestClass]
@@ -13,12 +12,12 @@ namespace Vit.Orm.SqlServer.MsTest
         public void Test_Get()
         {
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var user = dbContext.Get<User>(3);
                 Assert.AreEqual(3, user?.id);
             }
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+                using var dbContext = DataSource.CreateDbContext();
                 var user = dbContext.DbSet<User>().Get(5);
                 Assert.AreEqual(5, user?.id);
             }
@@ -30,7 +29,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_PlainQuery()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -54,7 +53,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_Where()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -87,7 +86,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_Select()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -98,13 +97,15 @@ namespace Vit.Orm.SqlServer.MsTest
 
 
             {
-                var userList = (from user in userQuery
-                                select new
-                                {
-                                    uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
-                                    uniqueId2 = $"{user.id}_{user.fatherId}_{user.motherId}"
-                                }).ToList();
+                var query =
+                    from user in userQuery
+                    select new
+                    {
+                        uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
+                        uniqueId2 = $"{user.id}_{user.fatherId}_{user.motherId}"
+                    };
 
+                var userList = query.ToList();
                 Assert.AreEqual(6, userList.Count);
                 Assert.AreEqual("1_4_6", userList.First().uniqueId1);
             }
@@ -114,7 +115,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_Count()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -134,7 +135,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_AllFeatures()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             #region SelectMany().Where().OrderBy().Skip().Take().ToExecuteString()
@@ -173,7 +174,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_FirstOrDefault()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -201,7 +202,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_First()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -233,7 +234,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_LastOrDefault()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -261,7 +262,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_Last()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -292,7 +293,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_Enumerable_ToArray()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -318,7 +319,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_Contains()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -346,7 +347,7 @@ namespace Vit.Orm.SqlServer.MsTest
         [TestMethod]
         public void Test_StringMethods()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             userQuery.ExecuteUpdate(row => new User
@@ -383,55 +384,13 @@ namespace Vit.Orm.SqlServer.MsTest
             }
         }
 
-        [TestMethod]
-        public void Test_DbFunction()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-
-            // select * from `User` as t0  where IIF(`t0`.`fatherId` is not null,true, false)
-            {
-                var query = userQuery.Where(u => DbFunction.Call<bool>("IIF", u.fatherId != null, true, false));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(3, userList.Last().id);
-            }
-
-            {
-                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime?>("datetime", "2021-01-01 00:00:00", "+2 hours"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(2, userList.First().id);
-            }
-            {
-                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime>("datetime", "2021-01-01 00:00:00", "+" + u.id + " hours"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-            }
-
-            // coalesce(parameter1,parameter2, …)
-            {
-                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-            }
-
-
-        }
 
 
 
         [TestMethod]
         public void Test_Distinct()
         {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
+            using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
 
             {
@@ -439,17 +398,19 @@ namespace Vit.Orm.SqlServer.MsTest
 
                 var sql = query.ToExecuteString();
                 var userList = query.ToList();
+                var ids = userList.Select(u => u.fatherId).ToList();
 
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(4, userList.First().fatherId);
-                Assert.AreEqual(null, userList.Last().fatherId);
+                Assert.AreEqual(3, ids.Count);
+                Assert.AreEqual(0, ids.Except(new int?[] { 4, 5, null }).Count());
             }
             {
                 var query = userQuery.Select(u => u.fatherId).Distinct();
 
                 var sql = query.ToExecuteString();
-                var fatherId = query.FirstOrDefault();
-                Assert.AreEqual(4, fatherId);
+                var ids = query.ToList();
+
+                Assert.AreEqual(3, ids.Count);
+                Assert.AreEqual(0, ids.Except(new int?[] { 4, 5, null }).Count());
             }
             {
                 var query = userQuery.Distinct();

+ 53 - 53
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Transaction_Test.cs → src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/CommonTest/Transaction_Test.cs

@@ -1,7 +1,7 @@
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest.CommonTest
 {
 
     [TestClass]
@@ -11,13 +11,9 @@ namespace Vit.Orm.Sqlite.MsTest
         [TestMethod]
         public void Test_Transaction()
         {
-            var dbName = System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_";
-            DataSource.CreateFormatedDbContext(dbName).Dispose();
-
-
             #region Transaction
             {
-                using var dbContext = DataSource.CreateDbContext(dbName);
+                using var dbContext = DataSource.CreateDbContext();
                 var userSet = dbContext.DbSet<User>();
 
                 Assert.AreEqual("u4", userSet.Get(4).name);
@@ -25,79 +21,79 @@ namespace Vit.Orm.Sqlite.MsTest
                 dbContext.Update(new User { id = 4, name = "u41" });
                 Assert.AreEqual("u41", userSet.Get(4).name);
 
-                using (var tran2 = dbContext.BeginTransaction())
+                using (var tran = dbContext.BeginTransaction())
                 {
                     dbContext.Update(new User { id = 4, name = "u42" });
                     Assert.AreEqual("u42", userSet.Get(4).name);
-                    //tran2.Rollback();
                 }
+                Assert.AreEqual("u41", userSet.Get(4).name);
 
+                using (var tran = dbContext.BeginTransaction())
+                {
+                    dbContext.Update(new User { id = 4, name = "u42" });
+                    Assert.AreEqual("u42", userSet.Get(4).name);
+                    tran.Rollback();
+                }
                 Assert.AreEqual("u41", userSet.Get(4).name);
-                using (var tran3 = dbContext.BeginTransaction())
+
+                using (var tran = dbContext.BeginTransaction())
                 {
                     dbContext.Update(new User { id = 4, name = "u43" });
                     Assert.AreEqual("u43", userSet.Get(4).name);
-
-                    tran3.Commit();
+                    tran.Commit();
                 }
-
                 Assert.AreEqual("u43", userSet.Get(4).name);
+
             }
             #endregion
+        }
 
 
-            #region Transaction Dispose
+        // can not test for db is not durable
+        //[TestMethod]
+        public void Test_Dispose()
+        {
             {
-                {
-                    using var dbContext = DataSource.CreateDbContext(dbName);
-                    var userSet = dbContext.DbSet<User>();
-
-                    var tran2 = dbContext.BeginTransaction();
-                    {
-                        dbContext.Update(new User { id = 4, name = "u42" });
-                        Assert.AreEqual("u42", userSet.Get(4).name);
-                        tran2.Commit();
-                    }
+                using var dbContext = DataSource.CreateDbContext();
+                var userSet = dbContext.DbSet<User>();
 
+                var tran2 = dbContext.BeginTransaction();
+                {
+                    dbContext.Update(new User { id = 4, name = "u42" });
                     Assert.AreEqual("u42", userSet.Get(4).name);
-
-                    var tran3 = dbContext.BeginTransaction();
-                    {
-                        dbContext.Update(new User { id = 4, name = "u43" });
-                        Assert.AreEqual("u43", userSet.Get(4).name);
-                    }
-                    Assert.AreEqual("u43", userSet.Get(4).name);
+                    tran2.Commit();
                 }
-                {
-                    using var dbContext = DataSource.CreateDbContext(dbName);
-                    var userSet = dbContext.DbSet<User>();
 
-                    Assert.AreEqual("u42", userSet.Get(4).name);
+                Assert.AreEqual("u42", userSet.Get(4).name);
+
+                var tran3 = dbContext.BeginTransaction();
+                {
+                    dbContext.Update(new User { id = 4, name = "u43" });
+                    Assert.AreEqual("u43", userSet.Get(4).name);
                 }
+                Assert.AreEqual("u43", userSet.Get(4).name);
             }
-            #endregion
-
+            {
+                using var dbContext = DataSource.CreateDbContext();
+                var userSet = dbContext.DbSet<User>();
 
+                //Assert.AreEqual("u42", userSet.Get(4).name);
+            }
 
         }
 
-
-
-
-        //[TestMethod]
+        [TestMethod]
         public void Test_NestedTransaction()
         {
-            var dbName = System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_";
-
-            #region
+            #region NestedTransaction
             {
-                using var dbContext = DataSource.CreateFormatedDbContext(dbName);
+                using var dbContext = DataSource.CreateDbContext();
                 var userSet = dbContext.DbSet<User>();
 
+                Assert.AreEqual("u4", userSet.Get(4).name);
+
                 using (var tran1 = dbContext.BeginTransaction())
                 {
-                    Assert.AreEqual("u4", userSet.Get(4).name);
-
                     dbContext.Update(new User { id = 4, name = "u41" });
                     Assert.AreEqual("u41", userSet.Get(4).name);
 
@@ -105,25 +101,29 @@ namespace Vit.Orm.Sqlite.MsTest
                     {
                         dbContext.Update(new User { id = 4, name = "u42" });
                         Assert.AreEqual("u42", userSet.Get(4).name);
-                        tran2.Rollback();
                     }
+                    Assert.AreEqual("u41", userSet.Get(4).name);
 
+                    using (var tran2 = dbContext.BeginTransaction())
+                    {
+                        dbContext.Update(new User { id = 4, name = "u42" });
+                        Assert.AreEqual("u42", userSet.Get(4).name);
+                        tran2.Rollback();
+                    }
                     Assert.AreEqual("u41", userSet.Get(4).name);
-                    using (var tran3 = dbContext.BeginTransaction())
+
+                    using (var tran2 = dbContext.BeginTransaction())
                     {
                         dbContext.Update(new User { id = 4, name = "u43" });
                         Assert.AreEqual("u43", userSet.Get(4).name);
+                        tran2.Commit();
                     }
-
                     Assert.AreEqual("u43", userSet.Get(4).name);
                 }
 
-                Assert.AreEqual("u43", userSet.Get(4).name);
+                Assert.AreEqual("u4", userSet.Get(4).name);
             }
             #endregion
-
-
-
         }
 
 

+ 6 - 14
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/DataSource.cs

@@ -3,7 +3,7 @@
 using Vit.Orm.Sql;
 using Vit.Extensions;
 
-namespace Vit.Orm.Sqlite.MsTest
+namespace Vit.Orm.MsTest
 {
     [Table("User")]
     public class User
@@ -21,29 +21,21 @@ namespace Vit.Orm.Sqlite.MsTest
 
     public class DataSource
     {
-        public static DbContext CreateDbContext(string dbName = "DataSource")
+        public static SqlDbContext CreateDbContext()
         {
-            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"{dbName}.db");
-            var dbContext = new SqlDbContext();
-            dbContext.UseSqlite($"data source={filePath}");
-            return dbContext;
-            //return CreateFormatedDbContext(dbName);
-        }
-
-        public static DbContext CreateFormatedDbContext(string dbName = "DataSource")
-        {
-            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"{dbName}.db");
+            var guid = Guid.NewGuid().ToString();
+            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"{guid}.sqlite.db");
             if (File.Exists(filePath)) File.Delete(filePath);
             File.WriteAllBytes(filePath, new byte[0]);
 
 
             var connectionString = $"data source={filePath}";
-            //connectionString = $"Data Source={dbName};Mode=Memory;Cache=Shared";
-            //connectionString = $"Data Source=:memory:";
 
             var dbContext = new SqlDbContext();
             dbContext.UseSqlite(connectionString);
 
+            dbContext.BeginTransaction();
+
             var userSet = dbContext.DbSet<User>();
             userSet.Create();
 

+ 56 - 0
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/DbFunction_Test.cs

@@ -0,0 +1,56 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+
+namespace Vit.Orm.MsTest
+{
+
+    [TestClass]
+    public class DbFunction_Test
+    {
+        [TestMethod]
+        public void Test_DbFunction()
+        {
+            using var dbContext = DataSource.CreateDbContext();
+            var userQuery = dbContext.Query<User>();
+
+
+            // select * from `User` as t0  where IIF(`t0`.`fatherId` is not null,true, false)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<bool>("IIF", u.fatherId != null, true, false));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(3, userList.Last().id);
+            }
+
+            {
+                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime?>("datetime", "2021-01-01 00:00:00", "+2 hours"));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(1, userList.Count);
+                Assert.AreEqual(2, userList.First().id);
+            }
+            {
+                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime>("datetime", "2021-01-01 00:00:00", "+" + u.id + " hours"));
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(6, userList.Count);
+                Assert.AreEqual(1, userList.First().id);
+            }
+
+            // coalesce(parameter1,parameter2, …)
+            {
+                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
+                var sql = query.ToExecuteString();
+                var userList = query.ToList();
+                Assert.AreEqual(3, userList.Count);
+                Assert.AreEqual(1, userList.First().id);
+            }
+
+
+        }
+
+
+    }
+}

+ 0 - 107
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_LeftJoin_ByGroupJoin_Test.cs

@@ -1,107 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-    [TestClass]
-    public class Query_LeftJoin_ByGroupJoin_Test
-    {
-        [TestMethod]
-        public void Test_LeftJoin_Demo()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    join father in userQuery on user.fatherId equals father.id into fathers
-                    from father in fathers.DefaultIfEmpty()
-                    where user.id > 2
-                    select new { user, father };
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(4, userList.Count);
-                Assert.AreEqual(3, userList[0].user.id);
-                Assert.AreEqual(5, userList[0].father?.id);
-                Assert.AreEqual(4, userList[1].user.id);
-                Assert.AreEqual(null, userList[1].father?.id);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                    userQuery.GroupJoin(
-                        userQuery
-                        , user => user.fatherId
-                        , father => father.id
-                        , (user, fathers) => new { user, fathers }
-                    )
-                    .SelectMany(
-                        row => row.fathers.DefaultIfEmpty()
-                        , (row, father) => new { row, father }
-                    )
-                    .Where(row2 => row2.row.user.id > 2)
-                    .Select(row2 => new { row2.row.user, row2.father });
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(4, userList.Count);
-                Assert.AreEqual(3, userList[0].user.id);
-                Assert.AreEqual(5, userList[0].father?.id);
-                Assert.AreEqual(4, userList[1].user.id);
-                Assert.AreEqual(null, userList[1].father?.id);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_LeftJoin_Complex()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    join father in userQuery on user.fatherId equals father.id into fathers
-                    from father in fathers.DefaultIfEmpty()
-                    join mother in userQuery on user.motherId equals mother.id into mothers
-                    from mother in mothers.DefaultIfEmpty()
-                    where user.id > 2
-                    orderby father.id descending
-                    select new
-                    {
-                        user,
-                        father,
-                        mother,
-                        testId = user.id + 100,
-                        hasFather = father != null ? true : false
-                    };
-
-                query = query.Skip(1).Take(2);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(2, userList.Count);
-
-                var first = userList.First();
-                Assert.AreEqual(4, first.user.id);
-                Assert.AreEqual(null, first.father?.id);
-                Assert.AreEqual(null, first.mother?.id);
-                Assert.AreEqual(104, first.testId);
-                Assert.AreEqual(false, first.hasFather);
-            }
-        }
-
-
-    }
-}

+ 0 - 200
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_LeftJoin_BySelectMany_Test.cs

@@ -1,200 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-    [TestClass]
-    public class Query_LeftJoin_BySelectMany_Test
-    {
-
-
-
-
-        [TestMethod]
-        public void Test_LeftJoin_Demo()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                        from user in userQuery
-                        from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                        where user.id > 2
-                        select new { user, father };
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(4, userList.Count);
-                Assert.AreEqual(3, userList[0].user.id);
-                Assert.AreEqual(5, userList[0].father?.id);
-                Assert.AreEqual(4, userList[1].user.id);
-                Assert.AreEqual(null, userList[1].father?.id);
-            }
-
-            // Lambda Expression
-            {
-                var query =
-                        userQuery.SelectMany(
-                            user => userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                            , (user, father) => new { user, father }
-                        )
-                        .Where(row => row.user.id > 2)
-                        .Select(row => new { row.user, row.father });
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(4, userList.Count);
-                Assert.AreEqual(3, userList[0].user.id);
-                Assert.AreEqual(5, userList[0].father?.id);
-                Assert.AreEqual(4, userList[1].user.id);
-                Assert.AreEqual(null, userList[1].father?.id);
-            }
-        }
-
-
-
-        [TestMethod]
-        public void Test_LeftJoin_Complex()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            // Linq Expresssion
-            {
-                var query =
-                    from user in userQuery
-                    from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                    from mother in userQuery.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
-                    where user.id > 2
-                    orderby father.id descending
-                    select new
-                    {
-                        user,
-                        father,
-                        mother,
-                        testId = user.id + 100,
-                        hasFather = father != null ? true : false
-                    };
-
-                query = query.Skip(1).Take(2);
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(2, userList.Count);
-
-                var first = userList.First();
-                Assert.AreEqual(4, first.user.id);
-                Assert.AreEqual(null, first.father?.id);
-                Assert.AreEqual(null, first.mother?.id);
-                Assert.AreEqual(104, first.testId);
-                Assert.AreEqual(false, first.hasFather);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_MultipleSelect()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id > 2 && father != null
-                             select new
-                             {
-                                 user,
-                                 father
-                             });
-
-                var userList = query.ToList();
-
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().user.id);
-            }
-
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id)
-                             from mother in userQuery.Where(mother => user.motherId == mother.id)
-                             select new
-                             {
-                                 uniqueId = user.id + "_" + father.id + "_" + mother.id,
-                                 uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
-                                 user,
-                                 user2 = user,
-                                 user3 = user,
-                                 father,
-                                 hasFather = user.fatherId != null ? true : false,
-                                 fatherName = father.name,
-                                 mother
-                             });
-
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(1, userList.First().user.id);
-                Assert.AreEqual(3, userList.Last().user.id);
-                Assert.AreEqual(5, userList.Last().father?.id);
-            }
-
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             from mother in userQuery.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
-                             select new
-                             {
-                                 user,
-                                 father,
-                                 userId = user.id + 100,
-                                 hasFather = user.fatherId != null ? true : false,
-                                 hasFather2 = father != null,
-                                 fatherName = father.name,
-                                 motherName = mother.name,
-                             });
-
-                var userList = query.ToList();
-
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First().user.id);
-                Assert.AreEqual(101, userList.First().userId);
-                Assert.AreEqual(6, userList.Last().user.id);
-                Assert.AreEqual(5, userList[2].father.id);
-            }
-
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id)
-                             from mother in userQuery.Where(mother => user.motherId == mother.id)
-                             where user.id > 1
-                             orderby father.id descending
-                             select new
-                             {
-                                 user,
-                                 father,
-                                 userId = user.id + 100,
-                                 hasFather = user.fatherId != null ? true : false,
-                                 hasFather2 = father != null,
-                                 fatherName = father.name,
-                                 motherName = mother.name,
-                             });
-
-                var userList = query.ToList();
-
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(5, userList.First().father?.id);
-                Assert.AreEqual(4, userList.Last().father?.id);
-            }
-
-        }
-
-
-    }
-}

+ 0 - 470
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Query_Test.cs

@@ -1,470 +0,0 @@
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Vit.Extensions.Linq_Extensions;
-using System.Data;
-using static Vit.Core.Util.XmlComment.MethodComment;
-
-namespace Vit.Orm.Sqlite.MsTest
-{
-
-    [TestClass]
-    public class Query_Test
-    {
-        [TestMethod]
-        public void Test_Get()
-        {
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var user = dbContext.Get<User>(3);
-                Assert.AreEqual(3, user?.id);
-            }
-            {
-                using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-                var user = dbContext.DbSet<User>().Get(5);
-                Assert.AreEqual(5, user?.id);
-            }
-        }
-
-
-
-
-        [TestMethod]
-        public void Test_PlainQuery()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-                Assert.AreEqual(6, userList.Last().id);
-            }
-
-
-            {
-                var userList = userQuery.Select(u => u.id).ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First());
-                Assert.AreEqual(6, userList.Last());
-            }
-        }
-
-
-
-        [TestMethod]
-        public void Test_Where()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.Where(u => u.id > 2).Where(m => m.id < 4).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-            {
-                var userList = userQuery.Where(u => u.id + 1 == 4).Where(m => m.fatherId == 5).ToList();
-                Assert.AreEqual(3, userList.First().id);
-            }
-            {
-                var userList = userQuery.Where(u => 4 == u.id + 1).Where(m => m.fatherId == 5).ToList();
-                Assert.AreEqual(3, userList.First().id);
-            }
-
-
-            {
-                var userList = userQuery.Where(u => u.birth == new DateTime(2021, 01, 01, 03, 00, 00)).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-            {
-                var userList = userQuery.Where(u => u.birth == DateTime.Parse("2021-01-01 01:00:00").AddHours(2)).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-        }
-
-        [TestMethod]
-        public void Test_Select()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.Select(u => u).Where(user => user.id > 2).Where(user => user.id < 4).Select(u => u).ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-            }
-
-
-            {
-                var userList = (from user in userQuery
-                                select new
-                                {
-                                    uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
-                                    uniqueId2 = $"{user.id}_{user.fatherId}_{user.motherId}"
-                                }).ToList();
-
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual("1_4_6", userList.First().uniqueId1);
-            }
-
-        }
-
-        [TestMethod]
-        public void Test_Count()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var count = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id > 2 && father == null
-                             select new
-                             {
-                                 father
-                             }).Count();
-
-                Assert.AreEqual(3, count);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_AllFeatures()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            #region SelectMany().Where().OrderBy().Skip().Take().ToExecuteString()
-            /*
-            users.SelectMany(
-                user => users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                , (user, father) => new {user = user, father = father}
-            ).Where(row => row.user.id > 2)
-            .Select(row => new {row.user })
-            .OrderBy(user=>user.id)
-            .Skip(1).Take(2);
-             */
-            {
-                var query = (from user in userQuery
-                             from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                             where user.id > 2
-                             orderby father.id, user.id descending
-                             select new
-                             {
-                                 user
-                             })
-                            .Skip(1).Take(2);
-
-                var sql = query.ToExecuteString();
-                var list = query.ToList();
-
-                Assert.AreEqual(2, list.Count);
-                Assert.AreEqual(5, list[0].user.id);
-                Assert.AreEqual(4, list[1].user.id);
-            }
-            #endregion
-        }
-
-
-
-        [TestMethod]
-        public void Test_FirstOrDefault()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.FirstOrDefault();
-                Assert.AreEqual(1, user?.id);
-            }
-
-            {
-                var user = userQuery.FirstOrDefault(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                var user = userQuery.FirstOrDefault(user => user.id == 13);
-                Assert.AreEqual(null, user?.id);
-            }
-
-            {
-                var user = userQuery.OrderByDescending(m => m.id).FirstOrDefault();
-                Assert.AreEqual(6, user?.id);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_First()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.First();
-                Assert.AreEqual(1, user?.id);
-            }
-
-            {
-                var user = userQuery.First(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                try
-                {
-                    var user = userQuery.First(user => user.id == 13);
-                    Assert.Fail("IQueryalbe.First should throw Exception");
-                }
-                catch (Exception ex)
-                {
-                }
-
-            }
-
-        }
-
-
-
-        [TestMethod]
-        public void Test_LastOrDefault()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.LastOrDefault();
-                Assert.AreEqual(6, user?.id);
-            }
-
-            {
-                var user = userQuery.LastOrDefault(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                var user = userQuery.LastOrDefault(user => user.id == 13);
-                Assert.AreEqual(null, user?.id);
-            }
-
-            {
-                var user = userQuery.OrderByDescending(m => m.id).LastOrDefault();
-                Assert.AreEqual(1, user?.id);
-            }
-        }
-
-
-        [TestMethod]
-        public void Test_Last()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var user = userQuery.Last();
-                Assert.AreEqual(6, user?.id);
-            }
-
-            {
-                var user = userQuery.Last(user => user.id == 3);
-                Assert.AreEqual(3, user?.id);
-            }
-
-            {
-                try
-                {
-                    var user = userQuery.Last(user => user.id == 13);
-                    Assert.Fail("IQueryalbe.First should throw Exception");
-                }
-                catch (Exception ex)
-                {
-                }
-
-            }
-
-        }
-
-        // Enumerable.ToArray
-        [TestMethod]
-        public void Test_Enumerable_ToArray()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.ToArray();
-                Assert.AreEqual(6, userList.Length);
-                Assert.AreEqual(1, userList.First().id);
-                Assert.AreEqual(6, userList.Last().id);
-            }
-
-
-            {
-                var userList = userQuery.Select(u => u.id).ToArray();
-                Assert.AreEqual(6, userList.Length);
-                Assert.AreEqual(1, userList.First());
-                Assert.AreEqual(6, userList.Last());
-            }
-        }
-
-
-
-        // Enumerable.Contains
-        // Queryable.Contains
-        [TestMethod]
-        public void Test_Contains()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var userList = userQuery.Where(u => new[] { 3, 5 }.Contains(u.id)).ToList();
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual(5, userList.Last().id);
-            }
-            {
-                var ids = new[] { 3, 5 }.AsEnumerable();
-                var userList = userQuery.Where(u => ids.Contains(u.id)).ToList();
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual(5, userList.Last().id);
-            }
-            {
-                var ids = new[] { 3, 5 }.AsQueryable();
-                var userList = userQuery.Where(u => ids.Contains(u.id)).ToList();
-                Assert.AreEqual(2, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual(5, userList.Last().id);
-            }
-        }
-
-        [TestMethod]
-        public void Test_StringMethods()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            userQuery.ExecuteUpdate(row => new User
-            {
-                name = "u|" + row.id + "|" + (row.fatherId.ToString() ?? "") + "|" + (row.motherId.ToString() ?? "")
-            });
-
-            // StartsWith
-            {
-                var query = userQuery.Where(u => u.name.StartsWith("u|3|5"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual("u|3|5|6", userList.First().name);
-            }
-            // EndsWith
-            {
-                var query = userQuery.Where(u => u.name.EndsWith("3|5|6"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual("u|3|5|6", userList.First().name);
-            }
-            // Contains
-            {
-                var query = userQuery.Where(u => u.name.Contains("|3|5|"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(3, userList.First().id);
-                Assert.AreEqual("u|3|5|6", userList.First().name);
-            }
-        }
-
-        [TestMethod]
-        public void Test_DbFunction()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-
-            // select * from `User` as t0  where IIF(`t0`.`fatherId` is not null,true, false)
-            {
-                var query = userQuery.Where(u => DbFunction.Call<bool>("IIF", u.fatherId != null, true, false));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(3, userList.Last().id);
-            }
-
-            {
-                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime?>("datetime", "2021-01-01 00:00:00", "+2 hours"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(1, userList.Count);
-                Assert.AreEqual(2, userList.First().id);
-            }
-            {
-                var query = userQuery.Where(u => u.birth == DbFunction.Call<DateTime>("datetime", "2021-01-01 00:00:00", "+" + u.id + " hours"));
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(6, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-            }
-
-            // coalesce(parameter1,parameter2, …)
-            {
-                var query = userQuery.Where(u => DbFunction.Call<int?>("coalesce", u.fatherId, u.motherId) != null);
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(1, userList.First().id);
-            }
-
-
-        }
-
-
-
-        [TestMethod]
-        public void Test_Distinct()
-        {
-            using var dbContext = DataSource.CreateFormatedDbContext(System.Reflection.MethodBase.GetCurrentMethod()?.Name ?? "_");
-            var userQuery = dbContext.Query<User>();
-
-            {
-                var query = userQuery.Select(u => new { u.fatherId }).Distinct();
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(3, userList.Count);
-                Assert.AreEqual(4, userList.First().fatherId);
-                Assert.AreEqual(null, userList.Last().fatherId);
-            }
-            {
-                var query = userQuery.Select(u => u.fatherId).Distinct();
-
-                var sql = query.ToExecuteString();
-                var fatherId = query.FirstOrDefault();
-                Assert.AreEqual(4, fatherId);
-            }
-            {
-                var query = userQuery.Distinct();
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(6, userList.Count);
-            }
-
-        }
-
-
-
-
-    }
-}

+ 1 - 0
src/Vit.Orm.Sqlite/Test/Vit.Orm.Sqlite.MsTest/Vit.Orm.Sqlite.MsTest.csproj

@@ -7,6 +7,7 @@
 
         <IsPackable>false</IsPackable>
         <IsTestProject>true</IsTestProject>
+        <RootNamespace>Vit.Orm.MsTest</RootNamespace>
     </PropertyGroup>
 
     <ItemGroup>

+ 5 - 3
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/DbContext_Extensions.cs

@@ -4,7 +4,7 @@ using System.Data;
 using Vit.Orm.Entity;
 using Vit.Orm.Entity.Dapper;
 using Vit.Orm.Sql;
-using Vit.Orm.Sqlite;
+using Vit.Orm.Sql.SqlTranslate;
 
 namespace Vit.Extensions
 {
@@ -12,14 +12,16 @@ namespace Vit.Extensions
     {
         public static SqlDbContext UseSqlite(this SqlDbContext dbContext, string ConnectionString)
         {
-            ISqlTranslator sqlTranslator = new SqlTranslator(dbContext);
+            ISqlTranslateService sqlTranslateService =   Vit.Orm.Sqlite.SqlTranslateService.Instance;
 
             Func<IDbConnection> createDbConnection = () => new Microsoft.Data.Sqlite.SqliteConnection(ConnectionString);
 
             Func<Type, IEntityDescriptor> getEntityDescriptor = (type) => EntityDescriptor.GetEntityDescriptor(type);
 
 
-            dbContext.Init(sqlTranslator: sqlTranslator, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
+            dbContext.Init(sqlTranslateService: sqlTranslateService, createDbConnection: createDbConnection, getEntityDescriptor: getEntityDescriptor);
+
+            dbContext.createTransactionScope = (dbContext) => new Vit.Orm.Sqlite.SqlTransactionScope(dbContext);
 
             return dbContext;
         }

+ 75 - 0
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTransactionScope.cs

@@ -0,0 +1,75 @@
+using System;
+using System.Data;
+
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.Transaction;
+
+using SqlTransaction = Microsoft.Data.Sqlite.SqliteTransaction;
+
+namespace Vit.Orm.Sqlite
+{
+    public class SqlTransactionScope : Vit.Orm.Sql.Transaction.SqlTransactionScope
+    {
+        int savePointCount = 0;
+        public DbTransactionWrap CreateTransactionSavePoint(IDbTransaction originalTransaction)
+        {
+            var savePointName = "tran" + savePointCount++;
+            return new DbTransactionWrapSavePoint(originalTransaction, savePointName);
+        }
+        public SqlTransactionScope(SqlDbContext dbContext) : base(dbContext)
+        {
+        }
+
+        public override IDbTransaction BeginTransaction()
+        {
+            DbTransactionWrap transactionWrap;
+            IDbTransaction originalTransaction = GetCurrentTransaction();
+            if (originalTransaction == null)
+            {
+                var dbConnection = dbContext.dbConnection;
+                if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
+                originalTransaction = dbConnection.BeginTransaction();
+
+                transactionWrap = new DbTransactionWrap(originalTransaction);
+            }
+            else
+            {
+                transactionWrap = CreateTransactionSavePoint(originalTransaction);
+            }
+
+            transactions.Push(transactionWrap);
+            return transactionWrap;
+        }
+
+    }
+
+    public class DbTransactionWrapSavePoint : DbTransactionWrap
+    {
+        public SqlTransaction sqlTran => (SqlTransaction)originalTransaction;
+        string savePoint;
+        public DbTransactionWrapSavePoint(IDbTransaction transaction, string savePoint) : base(transaction)
+        {
+            this.savePoint = savePoint;
+            sqlTran.Save(savePoint);
+        }
+
+        public override void Commit()
+        {
+            sqlTran.Release(savePoint);
+            TransactionState = ETransactionState.Committed;
+        }
+
+        public override void Dispose()
+        {
+            if (TransactionState == ETransactionState.Active)
+                sqlTran.Rollback(savePoint);
+            TransactionState = ETransactionState.Disposed;
+        }
+
+        public override void Rollback()
+        {
+            sqlTran.Rollback(savePoint);
+            TransactionState = ETransactionState.RolledBack;
+        }
+    }
+}

+ 261 - 0
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTranslateService.cs

@@ -0,0 +1,261 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.Entity;
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.SqlTranslate;
+using Vit.Orm.Sqlite.TranslateService;
+
+
+namespace Vit.Orm.Sqlite
+{
+    public class SqlTranslateService : Vit.Orm.Sql.SqlTranslate.SqlTranslateService
+    {
+        public static readonly SqlTranslateService Instance = new SqlTranslateService();
+
+        protected QueryTranslateService queryTranslateService;
+        protected ExecuteUpdateTranslateService executeUpdateTranslateService;
+        protected ExecuteDeleteTranslateService executeDeleteTranslateService;
+
+        public SqlTranslateService()
+        {
+            queryTranslateService = new QueryTranslateService(this);
+            executeUpdateTranslateService = new ExecuteUpdateTranslateService(this);
+            executeDeleteTranslateService = new ExecuteDeleteTranslateService(this);
+        }
+
+        #region EvalExpression
+        /// <summary>
+        /// read where or value or on
+        /// </summary>
+        /// <param name="arg"></param>
+        /// <returns></returns>
+        /// <exception cref="NotSupportedException"></exception>
+        /// <param name="data"></param>
+        public override string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
+        {
+            switch (data.nodeType)
+            {
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall methodCall = data;
+                        switch (methodCall.methodName)
+                        {
+                            // ##1 ToString
+                            case nameof(object.ToString):
+                                {
+                                    return $"cast({EvalExpression(arg, methodCall.@object)} as text)";
+                                }
+
+                            #region ##2 String method:  StartsWith EndsWith Contains
+                            case nameof(string.StartsWith): // String.StartsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like {EvalExpression(arg, value)}||'%'";
+                                }
+                            case nameof(string.EndsWith): // String.EndsWith
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like '%'||{EvalExpression(arg, value)}";
+                                }
+                            case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
+                                {
+                                    var str = methodCall.@object;
+                                    var value = methodCall.arguments[0];
+                                    return $"{EvalExpression(arg, str)} like '%'||{EvalExpression(arg, value)}||'%'";
+                                }
+                            #endregion
+                        }
+                        break;
+                    }
+
+                #region Read Value
+                case NodeType.Convert:
+                    {
+                        // cast( 4.1 as signed)
+
+                        ExpressionNode_Convert convert = data;
+
+                        Type targetType = convert.valueType?.ToType();
+
+                        if (targetType == typeof(object)) return EvalExpression(arg, convert.body);
+
+                        // Nullable
+                        if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
+
+                        string targetDbType = GetDbType(targetType);
+
+                        var sourceType = convert.body.Member_GetType();
+                        if (sourceType != null)
+                        {
+                            if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
+
+                            if (targetDbType == GetDbType(sourceType)) return EvalExpression(arg, convert.body);
+                        }
+
+                        if (targetDbType == "datetime")
+                        {
+                            return $"DATETIME({EvalExpression(arg, convert.body)})";
+                        }
+                        return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
+                    }
+                case nameof(ExpressionType.Add):
+                    {
+                        ExpressionNode_Binary binary = data;
+
+                        // ##1 String Add
+                        if (data.valueType?.ToType() == typeof(string))
+                        {
+                            return $"{EvalExpression(arg, binary.left)} || {EvalExpression(arg, binary.right)}";
+                        }
+
+                        // ##2 Numberic Add
+                        return $"{EvalExpression(arg, binary.left)} + {EvalExpression(arg, binary.right)}";
+                    }
+                case nameof(ExpressionType.Coalesce):
+                    {
+                        ExpressionNode_Binary binary = data;
+                        return $"COALESCE({EvalExpression(arg, binary.left)},{EvalExpression(arg, binary.right)})";
+                    }
+                    #endregion
+
+            }
+
+            return base.EvalExpression(arg, data);
+        }
+        #endregion
+
+
+
+        #region PrepareCreate
+        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
+        {
+            /* //sql
+CREATE TABLE user (
+  id int NOT NULL PRIMARY KEY,
+  name varchar(100) DEFAULT NULL,
+  birth date DEFAULT NULL,
+  fatherId int DEFAULT NULL,
+  motherId int DEFAULT NULL
+) ;
+              */
+            List<string> sqlFields = new();
+
+            // #1 primary key
+            sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY");
+
+            // #2 columns
+            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
+
+            return $@"
+CREATE TABLE {DelimitIdentifier(entityDescriptor.tableName)} (
+{string.Join(",\r\n  ", sqlFields)}
+)";
+
+
+            string GetColumnSql(IColumnDescriptor column)
+            {
+                bool nullable = false;
+
+                var type = column.type;
+                if (type.IsGenericType || type == typeof(string))
+                {
+                    nullable = true;
+                    type = TypeUtil.GetUnderlyingType(type);
+                }
+                // name varchar(100) DEFAULT NULL
+                return $"  {DelimitIdentifier(column.name)} {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
+            }
+        }
+        protected override string GetDbType(Type type)
+        {
+            if (type == typeof(DateTime))
+                return "datetime";
+
+            if (type == typeof(string))
+                return "text";
+
+            if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
+                return "real";
+
+            if (type == typeof(bool) || type.Name.ToLower().Contains("int")) return "integer";
+
+            throw new NotSupportedException("unsupported column type:" + type.Name);
+        }
+        #endregion
+
+
+        public override (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg)
+        {
+            /* //sql
+             insert into user(name,birth,fatherId,motherId) values('','','');
+             select seq from sqlite_sequence where name='user';
+              */
+            var entityDescriptor = arg.entityDescriptor;
+
+            var columns = entityDescriptor.allColumns;
+
+            // #1 GetSqlParams 
+            Func<object, Dictionary<string, object>> GetSqlParams = (entity) =>
+            {
+                var sqlParam = new Dictionary<string, object>();
+                foreach (var column in columns)
+                {
+                    var columnName = column.name;
+                    var value = column.Get(entity);
+
+                    sqlParam[columnName] = value;
+                }
+                return sqlParam;
+            };
+
+            #region #2 columns 
+            List<string> columnNames = new List<string>();
+            List<string> valueParams = new List<string>();
+            string columnName;
+
+            foreach (var column in columns)
+            {
+                columnName = column.name;
+
+                columnNames.Add(DelimitIdentifier(columnName));
+                valueParams.Add(GenerateParameterName(columnName));
+            }
+            #endregion
+
+            // #3 build sql
+            string sql = $@"insert into {DelimitIdentifier(entityDescriptor.tableName)}({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
+            //sql+=$"select seq from sqlite_sequence where name = '{tableName}'; ";
+            sql += "select null;";
+            return (sql, GetSqlParams);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = queryTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam, arg.dataReader);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeUpdateTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg, CombinedStream combinedStream)
+        {
+            string sql = executeDeleteTranslateService.BuildQuery(arg, combinedStream);
+            return (sql, arg.sqlParam);
+        }
+
+
+
+    }
+}

+ 0 - 139
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/SqlTranslator.cs

@@ -1,139 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Extensions.Linq_Extensions;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql;
-using Vit.Orm.Sqlite.Translator;
-
-
-namespace Vit.Orm.Sqlite
-{
-    public class SqlTranslator : Vit.Orm.Sql.Translator.SqlTranslator
-    {
-        public SqlTranslator(DbContext dbContext):base(dbContext) 
-        {
-        }
-
-        #region PrepareCreate
-        public override string PrepareCreate(IEntityDescriptor entityDescriptor)
-        {
-            /* //sql
-CREATE TABLE user (
-  id int NOT NULL PRIMARY KEY,
-  name varchar(100) DEFAULT NULL,
-  birth date DEFAULT NULL,
-  fatherId int DEFAULT NULL,
-  motherId int DEFAULT NULL
-) ;
-              */
-            List<string> sqlFields = new();
-
-            // #1 primary key
-            sqlFields.Add(GetColumnSql(entityDescriptor.key) + " PRIMARY KEY");
-
-            // #2 columns
-            entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
-
-            return $@"
-CREATE TABLE {DelimitIdentifier(entityDescriptor.tableName)} (
-{string.Join(",\r\n  ", sqlFields)}
-)";
-
-
-            string GetColumnSql(IColumnDescriptor column)
-            {
-                bool nullable = false;
-
-                var type = column.type;
-                if (type.IsGenericType || type == typeof(string))
-                {
-                    nullable = true;
-                    type = TypeUtil.GetUnderlyingType(type);
-                }
-                // name varchar(100) DEFAULT NULL
-                return $"  {DelimitIdentifier(column.name)} {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
-            }
-        }
-        protected override string GetDbType(Type type)
-        {
-            if (type == typeof(DateTime))
-                return "DATETIME";
-
-            if (type == typeof(string))
-                return "TEXT";
-
-            if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
-                return "REAL";
-
-            if (type == typeof(bool) || type.Name.ToLower().Contains("int")) return "INTEGER";
-
-            throw new NotSupportedException("unsupported column type:" + type.Name);
-        }
-        #endregion
-
-
-        public override (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) PrepareAdd<Entity>(DbSet<Entity> dbSet)
-        {
-            /* //sql
-             insert into user(name,birth,fatherId,motherId) values('','','');
-             select seq from sqlite_sequence where name='user';
-              */
-            var entityDescriptor = dbSet.entityDescriptor;
-
-            var columns = entityDescriptor.allColumns;
-
-            // #1 GetSqlParams 
-            Func<Entity, Dictionary<string, object>> GetSqlParams = (entity) =>
-            {
-                var sqlParam = new Dictionary<string, object>();
-                foreach (var column in columns)
-                {
-                    var columnName = column.name;
-                    var value = column.Get(entity);
-
-                    sqlParam[columnName] = value;
-                }
-                return sqlParam;
-            };
-
-            #region #2 columns 
-            List<string> columnNames = new List<string>();
-            List<string> valueParams = new List<string>();
-            string columnName;
-
-            foreach (var column in columns)
-            {
-                columnName = column.name;
-
-                columnNames.Add(DelimitIdentifier(columnName));
-                valueParams.Add(GenerateParameterName(columnName));
-            }
-            #endregion
-
-            // #3 build sql
-            string sql = $@"insert into {DelimitIdentifier(entityDescriptor.tableName)}({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
-            //sql+=$"select seq from sqlite_sequence where name = '{tableName}'; ";
-            sql += "select null;";
-            return (sql, GetSqlParams);
-        }
-
-        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(CombinedStream combinedStream)
-        {
-            var query = new ExecuteUpdateTranslator(this);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam);
-        }
-
-        public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(CombinedStream combinedStream)
-        {
-            var query = new ExecuteDeleteTranslator(this);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam);
-        }
-
-
-
-    }
-}

+ 59 - 0
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/TranslateService/ExecuteDeleteTranslateService.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.SqlTranslate;
+
+namespace Vit.Orm.Sqlite.TranslateService
+{
+    public class ExecuteDeleteTranslateService : BaseQueryTranslateService
+    {
+        /*
+WITH tmp AS (
+    select u.id 
+    from User u
+    left join User father on u.fatherId = father.id 
+    where u.id > 0
+)
+delete from User where id in ( SELECT id FROM tmp );
+         */
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            var sqlInner = base.BuildQuery(arg, stream);
+
+
+            var NewLine = "\r\n";
+            var keyName = entityDescriptor.keyName;
+            var tableName = entityDescriptor.tableName;
+
+
+            var sql = $"WITH tmp AS ( {NewLine}";
+            sql += sqlInner;
+
+            sql += $"{NewLine}){NewLine}";
+            sql += $"delete from {sqlTranslator.DelimitIdentifier(tableName)} ";
+
+            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
+
+            return sql;
+        }
+
+
+        public ExecuteDeleteTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+
+            // primary key
+            return $"{prefix} {sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}";
+        }
+
+
+
+    }
+}

+ 22 - 25
src/Vit.Orm.Mysql/Vit.Orm.Mysql/Translator/ExecuteUpdateTranslator.cs → src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/TranslateService/ExecuteUpdateTranslateService.cs

@@ -3,13 +3,11 @@ using System.Collections.Generic;
 using System.Linq;
 
 using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql.Translator;
+using Vit.Orm.Sql.SqlTranslate;
 
-namespace Vit.Orm.Mysql.Translator
+namespace Vit.Orm.Sqlite.TranslateService
 {
-    public class ExecuteUpdateTranslator : BaseQueryTranslator
+    public class ExecuteUpdateTranslateService : BaseQueryTranslateService
     {
         /*
 
@@ -28,11 +26,14 @@ where id in ( SELECT id FROM tmp );
 --- single
 UPDATE User SET name = 'u'||id  where id > 0;
          */
-        public override string BuildQuery(CombinedStream stream)
+        public override string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
         {
-            var sqlInner = base.BuildQuery(stream);
+            var sqlInner = base.BuildQuery(arg, stream);
 
 
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+            var columnsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate?.memberArgs;
+
             var NewLine = "\r\n";
             var keyName = entityDescriptor.keyName;
             var tableName = entityDescriptor.tableName;
@@ -42,47 +43,43 @@ UPDATE User SET name = 'u'||id  where id > 0;
             sql += sqlInner;
 
             sql += $"{NewLine}){NewLine}";
-            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)} ";
+            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)}{NewLine}";
+            sql += $"Set ";
+
+            var sqlToUpdateCols = columnsToUpdate
+                .Select(m => m.name)
+                .Select(name => $"{NewLine}  {sqlTranslator.DelimitIdentifier(name)} = (SELECT {sqlTranslator.DelimitIdentifier("_" + name)} FROM tmp WHERE tmp.{sqlTranslator.DelimitIdentifier(keyName)} ={sqlTranslator.GetSqlField(tableName, keyName)} )");
 
-            var sqlToUpdateCols = columnsToUpdate.Select(m => m.name).Select(name => $"{NewLine}  SET {sqlTranslator.DelimitIdentifier(name)} =  ( SELECT {sqlTranslator.DelimitIdentifier("_" + name)} FROM tmp WHERE tmp.{sqlTranslator.DelimitIdentifier(keyName)} ={sqlTranslator.GetSqlField(tableName, keyName)} )");
             sql += string.Join(",", sqlToUpdateCols);
 
             sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
 
             return sql;
         }
+ 
 
-
-        List<MemberBind> columnsToUpdate;
-        IEntityDescriptor entityDescriptor;
-
-        public ExecuteUpdateTranslator(SqlTranslator sqlTranslator) : base(sqlTranslator)
+        public ExecuteUpdateTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
         {
         }
 
-        protected override string ReadSelect(CombinedStream stream)
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
         {
-            var fieldsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate;
+            var entityDescriptor = arg.dbContext.GetEntityDescriptor(arg.resultEntityType);
+            var columnsToUpdate = (stream as StreamToUpdate) ?.fieldsToUpdate?.memberArgs;
 
-            columnsToUpdate = (fieldsToUpdate?.constructorArgs ?? new()).AsQueryable().Concat(fieldsToUpdate?.memberArgs ?? new()).ToList();
             if (columnsToUpdate?.Any() != true) throw new ArgumentException("can not get columns to update");
 
-
-            var entityType = fieldsToUpdate.New_GetType();
-            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-            if (entityDescriptor == null) throw new ArgumentException("Entity can not be updated");
-
-
             var sqlFields = new List<string>();
 
             foreach (var column in columnsToUpdate)
             {
-                sqlFields.Add($"({ReadEval(column.value)}) as {sqlTranslator.DelimitIdentifier("_" + column.name)}");
+                sqlFields.Add($"({sqlTranslator.EvalExpression( arg,  column.value)}) as {sqlTranslator.DelimitIdentifier("_" + column.name)}");
             }
 
             // primary key
             sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}");
-            return String.Join(",", sqlFields);
+
+            return prefix + " " + String.Join(",", sqlFields);
         }
 
 

+ 0 - 66
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/Translator/ExecuteDeleteTranslator.cs

@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql.Translator;
-
-namespace Vit.Orm.Sqlite.Translator
-{
-    public class ExecuteDeleteTranslator : BaseQueryTranslator
-    {
-        /*
-WITH tmp AS (
-    select u.id 
-    from User u
-    left join User father on u.fatherId = father.id 
-    where u.id > 0
-)
-delete from User where id in ( SELECT id FROM tmp );
-         */
-        public override string BuildQuery(CombinedStream stream)
-        {
-            var sqlInner = base.BuildQuery(stream);
-
-
-            var NewLine = "\r\n";
-            var keyName = entityDescriptor.keyName;
-            var tableName = entityDescriptor.tableName;
-
-
-            var sql = $"WITH tmp AS ( {NewLine}";
-            sql += sqlInner;
-
-            sql += $"{NewLine}){NewLine}";
-            sql += $"delete from {sqlTranslator.DelimitIdentifier(tableName)} ";
-
-            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
-
-            return sql;
-        }
-
-
-
-        IEntityDescriptor entityDescriptor;
-
-        public ExecuteDeleteTranslator(SqlTranslator sqlTranslator) : base(sqlTranslator)
-        {
-        }
-
-        protected override string ReadSelect(CombinedStream stream)
-        {
-            var entityType = (stream.source as SourceStream)?.GetEntityType();
-            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-            if (entityDescriptor == null) throw new ArgumentException("Entity can not be deleted");
-
-            var sqlFields = new List<string>();
-
-            // primary key
-            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as `{entityDescriptor.keyName}`");
-            return String.Join(",", sqlFields);
-        }
-
-
-
-    }
-}

+ 0 - 91
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/Translator/ExecuteUpdateTranslator.cs

@@ -1,91 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Orm.Entity;
-using Vit.Orm.Sql.Translator;
-
-namespace Vit.Orm.Sqlite.Translator
-{
-    public class ExecuteUpdateTranslator : BaseQueryTranslator
-    {
-        /*
-
--- multiple
-WITH tmp AS (
-    select   ('u' || u.id || '_' || COALESCE(father.id,'') ) as _name , u.id 
-    from User u
-    left join User father on u.fatherId = father.id 
-    where u.id > 0
-)
-UPDATE User  
-  SET name =  ( SELECT _name FROM tmp WHERE tmp.id =User.id )
-where id in ( SELECT id FROM tmp );
-
-
---- single
-UPDATE User SET name = 'u'||id  where id > 0;
-         */
-        public override string BuildQuery(CombinedStream stream)
-        {
-            var sqlInner = base.BuildQuery(stream);
-
-
-            var NewLine = "\r\n";
-            var keyName = entityDescriptor.keyName;
-            var tableName = entityDescriptor.tableName;
-
-
-            var sql = $"WITH tmp AS ( {NewLine}";
-            sql += sqlInner;
-
-            sql += $"{NewLine}){NewLine}";
-            sql += $"UPDATE {sqlTranslator.DelimitIdentifier(tableName)} ";
-
-            var sqlToUpdateCols = columnsToUpdate.Select(m => m.name).Select(name => $"{NewLine}  SET {sqlTranslator.DelimitIdentifier(name)} =  ( SELECT {sqlTranslator.DelimitIdentifier("_" + name)} FROM tmp WHERE tmp.{sqlTranslator.DelimitIdentifier(keyName)} ={sqlTranslator.GetSqlField(tableName, keyName)} )");
-            sql += string.Join(",", sqlToUpdateCols);
-
-            sql += $"{NewLine}where {sqlTranslator.DelimitIdentifier(keyName)} in ( SELECT {sqlTranslator.DelimitIdentifier(keyName)} FROM tmp ); {NewLine}";
-
-            return sql;
-        }
-
-
-        List<MemberBind> columnsToUpdate;
-        IEntityDescriptor entityDescriptor;
-
-        public ExecuteUpdateTranslator(SqlTranslator sqlTranslator) : base(sqlTranslator)
-        {
-        }
-
-        protected override string ReadSelect(CombinedStream stream)
-        {
-            var fieldsToUpdate = (stream as StreamToUpdate)?.fieldsToUpdate;
-
-            columnsToUpdate = (fieldsToUpdate?.constructorArgs ?? new()).AsQueryable().Concat(fieldsToUpdate?.memberArgs ?? new()).ToList();
-            if (columnsToUpdate?.Any() != true) throw new ArgumentException("can not get columns to update");
-
-
-            var entityType = fieldsToUpdate.New_GetType();
-            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-            if (entityDescriptor == null) throw new ArgumentException("Entity can not be updated");
-
-
-            var sqlFields = new List<string>();
-
-            foreach (var column in columnsToUpdate)
-            {
-                sqlFields.Add($"({ReadEval(column.value)}) as {sqlTranslator.DelimitIdentifier("_" + column.name)}");
-            }
-
-            // primary key
-            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.source.alias, entityDescriptor.keyName)} as {sqlTranslator.DelimitIdentifier(entityDescriptor.keyName)}");
-            return String.Join(",", sqlFields);
-        }
-
-
-
-    }
-}

+ 1 - 1
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite/Vit.Orm.Sqlite.csproj

@@ -11,7 +11,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.17" />
+        <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.6" />
     </ItemGroup>
 
     <ItemGroup>

+ 18 - 15
src/Vit.Orm/DbContext.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Data;
 using System.Linq;
+using System.Transactions;
 
 using Vit.Linq.ExpressionTree;
 using Vit.Orm.Entity;
@@ -23,6 +24,7 @@ namespace Vit.Orm
             if (dbSetMap.TryGetValue(entityType, out var dbSet)) return dbSet;
 
             dbSet = dbSetCreator(entityType);
+            if (dbSet == null) return null;
             dbSetMap[entityType] = dbSet;
             return dbSet;
 
@@ -37,34 +39,35 @@ namespace Vit.Orm
         public virtual IEntityDescriptor GetEntityDescriptor(Type entityType) => DbSet(entityType)?.entityDescriptor;
 
 
+        // #1 Schema :  Create
+        public virtual void Create<Entity>() => throw new NotImplementedException();
 
-        public virtual void Create<Entity>() => DbSet<Entity>().Create();
 
-        public virtual Entity Add<Entity>(Entity entity) => DbSet<Entity>().Add(entity);
-        public virtual void AddRange<Entity>(IEnumerable<Entity> entitys) => DbSet<Entity>().AddRange(entitys);
+        // #1 Create :  Add AddRange
+        public virtual Entity Add<Entity>(Entity entity) => throw new NotImplementedException();
+        public virtual void AddRange<Entity>(IEnumerable<Entity> entitys) => throw new NotImplementedException();
 
+        // #2 Retrieve : Get Query
+        public virtual Entity Get<Entity>(object keyValue) => throw new NotImplementedException();
+        public virtual IQueryable<Entity> Query<Entity>() => throw new NotImplementedException();
 
-        public virtual Entity Get<Entity>(object keyValue) => DbSet<Entity>().Get(keyValue);
-        public virtual IQueryable<Entity> Query<Entity>() => DbSet<Entity>().Query();
 
+        // #3 Update: Update UpdateRange
+        public virtual int Update<Entity>(Entity entity) => throw new NotImplementedException();
+        public virtual int UpdateRange<Entity>(IEnumerable<Entity> entitys) => throw new NotImplementedException();
 
 
-        public virtual int Update<Entity>(Entity entity) => DbSet<Entity>().Update(entity);
-        public virtual int UpdateRange<Entity>(IEnumerable<Entity> entitys) => DbSet<Entity>().UpdateRange(entitys);
+        // #4 Delete : Delete DeleteRange DeleteByKey DeleteByKeys
+        public virtual int Delete<Entity>(Entity entity) => throw new NotImplementedException();
+        public virtual int DeleteRange<Entity>(IEnumerable<Entity> entitys) => throw new NotImplementedException();
 
 
+        public virtual int DeleteByKey<Entity>(object keyValue) => throw new NotImplementedException();
+        public virtual int DeleteByKeys<Entity, Key>(IEnumerable<Key> keys) => throw new NotImplementedException();
 
-        public virtual int Delete<Entity>(Entity entity) => DbSet<Entity>().Delete(entity);
-        public virtual int DeleteRange<Entity>(IEnumerable<Entity> entitys) => DbSet<Entity>().DeleteRange(entitys);
 
 
-        public virtual int DeleteByKey<Entity>(object keyValue) => DbSet<Entity>().DeleteByKey(keyValue);
-        public virtual int DeleteByKeys<Entity, Key>(IEnumerable<Key> keys) => DbSet<Entity>().DeleteByKeys(keys);
 
-        public virtual IDbTransaction BeginTransaction()
-        {
-            throw new NotImplementedException();
-        }
         public virtual void Dispose()
         {
         }

+ 8 - 4
src/Vit.Orm/Entity/Dapper/EntityDescriptor.cs

@@ -26,6 +26,8 @@ namespace Vit.Orm.Entity.Dapper
 
         EntityDescriptor(Type entityType)
         {
+            this.entityType = entityType;
+
             tableName = GetTableName(entityType);
 
             var entityProperties = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance) ?? new PropertyInfo[0];
@@ -39,7 +41,9 @@ namespace Vit.Orm.Entity.Dapper
             allColumns = new List<IColumnDescriptor> { key }.Concat(columns).ToArray();
         }
 
-        public string tableName { get; private set; }
+
+        public Type entityType { get; protected set; }
+        public string tableName { get; protected set; }
 
         /// <summary>
         /// primary key name
@@ -49,15 +53,15 @@ namespace Vit.Orm.Entity.Dapper
         /// <summary>
         /// primary key
         /// </summary>
-        public IColumnDescriptor key { get; private set; }
+        public IColumnDescriptor key { get; protected set; }
 
         /// <summary>
         /// not include primary key
         /// </summary>
-        public IColumnDescriptor[] columns { get; private set; }
+        public IColumnDescriptor[] columns { get; protected set; }
 
 
-        public IColumnDescriptor[] allColumns { get; private set; }
+        public IColumnDescriptor[] allColumns { get; protected set; }
 
 
     }

+ 5 - 1
src/Vit.Orm/Entity/IEntityDescriptor.cs

@@ -1,7 +1,11 @@
-namespace Vit.Orm.Entity
+using System;
+
+namespace Vit.Orm.Entity
 {
     public interface IEntityDescriptor
     {
+        Type entityType { get; }
+
         string tableName { get; }
         string keyName { get; }
         /// <summary>

+ 38 - 75
src/Vit.Orm/Sql/DataReader/EntityReader.cs

@@ -6,6 +6,7 @@ using System.Reflection;
 
 using Vit.Linq.ExpressionTree;
 using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.Sql.SqlTranslate;
 
 namespace Vit.Orm.Sql.DataReader
 {
@@ -17,7 +18,7 @@ namespace Vit.Orm.Sql.DataReader
         protected List<IArgReader> entityArgReaders = new List<IArgReader>();
         protected Delegate lambdaCreateEntity;
 
-        public string BuildSelect(Type entityType, ISqlTranslator sqlTranslator, ExpressionConvertService convertService, ExpressionNode selectedFields)
+        public string BuildSelect(QueryTranslateArgument arg, Type entityType, ISqlTranslateService sqlTranslateService, ExpressionConvertService convertService, ExpressionNode selectedFields)
         {
             this.entityType = entityType;
 
@@ -28,7 +29,7 @@ namespace Vit.Orm.Sql.DataReader
                 {
                     ExpressionNode_Member member = node;
 
-                    var argName = GetArgument(sqlTranslator, member);
+                    var argName = GetArgument(arg, sqlTranslateService, member);
 
                     if (argName != null)
                     {
@@ -38,12 +39,17 @@ namespace Vit.Orm.Sql.DataReader
                 else if (node?.nodeType == NodeType.MethodCall)
                 {
                     ExpressionNode_MethodCall methodCall = node;
-
-                    var argName = GetArgument(sqlTranslator, methodCall);
-
-                    if (argName != null)
+                    if (methodCall.methodCall_typeName == "Enumerable")
                     {
-                        return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                        string argName = null;
+
+                        var sqlField = sqlTranslateService.EvalExpression(arg, node);
+                        var fieldType = methodCall.MethodCall_GetReturnType();
+                        argName = GetArgument(sqlField, fieldType);
+                        if (argName != null)
+                        {
+                            return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                        }
                     }
                 }
                 return default;
@@ -61,7 +67,8 @@ namespace Vit.Orm.Sql.DataReader
             #endregion
 
             // sqlFields
-            return String.Join(", ", sqlFields);
+            var fields = sqlFields.Select((f, index) => f + " as c" + index);
+            return String.Join(", ", fields);
         }
 
 
@@ -86,10 +93,16 @@ namespace Vit.Orm.Sql.DataReader
             return list;
         }
 
-        protected string GetArgument(ISqlTranslator sqlTranslator, ExpressionNode_Member member)
+        protected string GetArgument(QueryTranslateArgument arg, ISqlTranslateService sqlTranslator, ExpressionNode_Member member)
         {
+
+            // 1: {"nodeType":"Member","parameterName":"a0","memberName":"id"}
+            // 2: {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
+            var tableName = member.objectValue?.parameterName ?? member.parameterName;
+
+
             // tableName_fieldName   tableName_
-            var argUniqueKey = $"arg_{member.objectValue?.parameterName ?? member.parameterName}_{member.memberName}";
+            var argUniqueKey = $"arg_{tableName}_{member.memberName}";
 
             IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
 
@@ -103,88 +116,38 @@ namespace Vit.Orm.Sql.DataReader
                 if (isValueType)
                 {
                     // Value arg
-                    string sqlFieldName = sqlTranslator.GetSqlField(member);
+                    string sqlFieldName = sqlTranslator.GetSqlField(member,arg.dbContext);
                     argReader = new ValueReader(this, argType, argUniqueKey, argName, sqlFieldName);
                 }
                 else
                 {
                     // Entity arg
-                    argReader = new ModelReader(this, sqlTranslator, member, argUniqueKey, argName, argType);
+                    var entityDescriptor = arg.dbContext.GetEntityDescriptor(argType);
+
+                    argReader = new ModelReader(this, sqlTranslator, tableName, argUniqueKey, argName, argType, entityDescriptor);
                 }
                 entityArgReaders.Add(argReader);
             }
             return argReader.argName;
         }
-        protected string GetArgument(ISqlTranslator sqlTranslator, ExpressionNode_MethodCall methodCall)
-        {
-            var functionName = methodCall.methodName;
-            switch (methodCall.methodName)
-            {
-                case nameof(Enumerable.Count):
-                    {
-                        var stream = methodCall.arguments[0] as ExpressionNode_Member;
-                        if (stream?.nodeType == NodeType.Member && stream.parameterName != null && stream.memberName == null)
-                        {
-                            var tableName = stream.parameterName;
-                            var columnName = stream.memberName;
-
-                            var argUniqueKey = $"argFunc_{functionName}_{tableName}_{columnName}";
-
-                            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
-
-                            if (argReader == null)
-                            {
-                                var argName = "arg_" + entityArgReaders.Count;
 
-                                var argType = typeof(int);
-
-                                // Value arg
-                                string sqlFieldName = sqlTranslator.GetSqlField_Aggregate(functionName,tableName, columnName: columnName);
-                                argReader = new ValueReader(this, argType, argUniqueKey, argName, sqlFieldName);
-
-                                entityArgReaders.Add(argReader);
-                            }
-                            return argReader.argName;
-                        }
-                    }
-                    break;
-                case nameof(Enumerable.Max) or nameof(Enumerable.Min) or nameof(Enumerable.Sum) or nameof(Enumerable.Average) when methodCall.arguments.Length == 2:
-                    {
-                        var stream = methodCall.arguments[0] as ExpressionNode_Member;
-                        if (stream?.nodeType == NodeType.Member && stream.parameterName != null && stream.memberName == null)
-                        {
-                            var lambdaFieldSelect = methodCall.arguments[1] as ExpressionNode_Lambda;
-                            if (lambdaFieldSelect?.body?.nodeType == NodeType.Member)
-                            {
-                                var tableName = stream.parameterName;
-                                string columnName = lambdaFieldSelect.body.memberName;
-
-                                var argUniqueKey = $"argFunc_{functionName}_{tableName}_{columnName}";
-
-                                IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
-
-                                if (argReader == null)
-                                {
-                                    var argName = "arg_" + entityArgReaders.Count;
+        protected string GetArgument(string sqlField, Type fieldType)
+        {
+            var argUniqueKey = $"argFunc_{sqlField}";
 
-                                    var argType = methodCall.MethodCall_GetReturnType();
+            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
 
-                                    // Value arg
-                                    string sqlFieldName = sqlTranslator.GetSqlField_Aggregate(functionName,tableName, columnName: columnName);
-                                    argReader = new ValueReader(this, argType, argUniqueKey, argName, sqlFieldName);
+            if (argReader == null)
+            {
+                var argName = "arg_" + entityArgReaders.Count;
 
-                                    entityArgReaders.Add(argReader);
-                                }
-                                return argReader.argName;
-                            }
-                        }
-                    }
-                    break;
+                argReader = new ValueReader(this, fieldType, argUniqueKey, argName, sqlField);
 
+                entityArgReaders.Add(argReader);
             }
-            //throw new NotSupportedException("[CollectionStream] unexpected method call : " + methodCall.methodName);
-            return default;
+            return argReader.argName;
         }
+ 
 
 
     }

+ 3 - 7
src/Vit.Orm/Sql/DataReader/EntityReader/ModelReader.cs

@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using System.Data;
 
 using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.Entity;
+using Vit.Orm.Sql.SqlTranslate;
 
 namespace Vit.Orm.Sql.DataReader
 {
@@ -14,18 +16,12 @@ namespace Vit.Orm.Sql.DataReader
 
         List<EntityPropertyReader> proppertyReaders = new();
 
-        public ModelReader(EntityReader entityReader, ISqlTranslator sqlTranslator, ExpressionNode_Member member, string argUniqueKey, string argName, Type argType)
+        public ModelReader(EntityReader entityReader, ISqlTranslateService sqlTranslator, string tableName, string argUniqueKey, string argName, Type argType, IEntityDescriptor entityDescriptor)
         {
             this.argUniqueKey = argUniqueKey;
             this.argName = argName;
             this.argType = argType;
 
-            var entityDescriptor = sqlTranslator.GetEntityDescriptor(argType);
-
-            // 1: {"nodeType":"Member","parameterName":"a0","memberName":"id"}
-            // 2: {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
-            var tableName = member.objectValue?.parameterName ?? member.parameterName;
-
             // ##1 key
             string sqlFieldName = sqlTranslator.GetSqlField(tableName, entityDescriptor.keyName);
             proppertyReaders.Add(new EntityPropertyReader(entityReader, entityDescriptor.key, true, sqlFieldName));

+ 327 - 56
src/Vit.Orm/Sql/SqlDbContext.cs

@@ -3,13 +3,20 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
-using System.Data.Common;
+using System.Linq;
+using System.Linq.Expressions;
 
+using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq;
 using Vit.Orm.Entity;
+using Vit.Orm.Sql.Transaction;
+using Vit.Orm.Sql.SqlTranslate;
 
 namespace Vit.Orm.Sql
 {
-    public class SqlDbContext : DbContext
+    public partial class SqlDbContext : DbContext
     {
         protected Func<IDbConnection> createDbConnection { get; set; }
         protected IDbConnection _dbConnection;
@@ -17,8 +24,9 @@ namespace Vit.Orm.Sql
         {
             base.Dispose();
 
-            // dispose transactions
-            DisposeTransactions();
+
+            transactionScope?.Dispose();
+            transactionScope = null;
 
             _dbConnection?.Dispose();
             _dbConnection = null;
@@ -26,12 +34,12 @@ namespace Vit.Orm.Sql
         public virtual IDbConnection dbConnection => _dbConnection ??= createDbConnection();
 
 
-        public ISqlTranslator sqlTranslator { get; private set; }
+        public virtual ISqlTranslateService sqlTranslateService { get; private set; }
 
 
-        public void Init(ISqlTranslator sqlTranslator, Func<IDbConnection> createDbConnection, Func<Type, IEntityDescriptor> getEntityDescriptor)
+        public virtual void Init(ISqlTranslateService sqlTranslateService, Func<IDbConnection> createDbConnection, Func<Type, IEntityDescriptor> getEntityDescriptor)
         {
-            this.sqlTranslator = sqlTranslator;
+            this.sqlTranslateService = sqlTranslateService;
             this.createDbConnection = createDbConnection;
 
             this.dbSetCreator = (entityType) =>
@@ -40,89 +48,352 @@ namespace Vit.Orm.Sql
                 return SqlDbSetConstructor.CreateDbSet(this, entityType, entityDescriptor);
             };
         }
-        public void Init(ISqlTranslator sqlTranslator, Func<IDbConnection> createDbConnection, Func<Type, IDbSet> dbSetCreator)
+        public virtual void Init(ISqlTranslateService sqlTranslator, Func<IDbConnection> createDbConnection, Func<Type, IDbSet> dbSetCreator)
         {
-            this.sqlTranslator = sqlTranslator;
+            this.sqlTranslateService = sqlTranslator;
             this.createDbConnection = createDbConnection;
 
             this.dbSetCreator = dbSetCreator;
         }
 
-        protected Stack<TransactionWrap> transactions;
-        protected IDbTransaction GetCurrentTransaction()
+        #region #0 Schema :  Create
+
+        public override void Create<Entity>()
         {
-            if (transactions == null) return null;
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+
+
+            string sql = sqlTranslateService.PrepareCreate(entityDescriptor);
+            Execute(sql: sql);
+        }
+        #endregion
+
+
+        #region #1 Create :  Add AddRange
 
-            while (transactions.Count > 0)
+        public override Entity Add<Entity>(Entity entity)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            // #1 prepare sql
+            (string sql, Func<object, Dictionary<string, object>> GetSqlParams) = sqlTranslateService.PrepareAdd(arg);
+
+            // #2 get sql params
+            var sqlParam = GetSqlParams(entity);
+
+            // #3 execute
+            var newKeyValue = ExecuteScalar(sql: sql, param: (object)sqlParam);
+
+            if (newKeyValue != null)
             {
-                var tran = transactions.Peek();
-                if (tran?.TransactionState == TransactionWrap.ETransactionState.Active) return tran.originalTransaction;
-                transactions.Pop();
+                var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
+                newKeyValue = TypeUtil.ConvertToUnderlyingType(newKeyValue, keyType);
+                entityDescriptor.key.Set(entity, newKeyValue);
             }
-            return null;
+            return entity;
         }
-        protected void DisposeTransactions()
+        public override void AddRange<Entity>(IEnumerable<Entity> entitys)
         {
-            if (transactions == null) return;
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            // #1 prepare sql
+            (string sql, Func<object, Dictionary<string, object>> GetSqlParams) = sqlTranslateService.PrepareAdd(arg);
+
+            // #2 execute
+            var affectedRowCount = 0;
 
-            while (transactions.Count > 0)
+            var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
+            foreach (var entity in entitys)
             {
-                var transaction = transactions.Pop();
-                if (transaction?.TransactionState != TransactionWrap.ETransactionState.Disposed)
+                var sqlParam = GetSqlParams(entity);
+                var newKeyValue = ExecuteScalar(sql: sql, param: (object)sqlParam);
+                if (newKeyValue != null)
                 {
-                    transaction?.Dispose();
+                    newKeyValue = TypeUtil.ConvertToUnderlyingType(newKeyValue, keyType);
+                    entityDescriptor.key.Set(entity, newKeyValue);
                 }
+                affectedRowCount++;
             }
-            transactions = null;
         }
-        public override IDbTransaction BeginTransaction()
-        {
-            if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
 
-            var transaction = dbConnection.BeginTransaction();
+        #endregion
+
 
-            transactions ??= new();
-            var wrap = new TransactionWrap(transaction);
-            transactions.Push(wrap);
-            return wrap;
-        }
 
-        public class TransactionWrap : IDbTransaction
+        #region #2 Retrieve : Get Query
+
+        public override Entity Get<Entity>(object keyValue)
         {
-            public enum ETransactionState
-            {
-                Active, Committed, RolledBack, Disposed
-            }
-            public ETransactionState TransactionState { get; private set; } = ETransactionState.Active;
-            public TransactionWrap(IDbTransaction transaction)
-            {
-                originalTransaction = transaction;
-            }
-            public IDbTransaction originalTransaction;
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
 
-            public IDbConnection Connection => originalTransaction.Connection;
 
-            public System.Data.IsolationLevel IsolationLevel => originalTransaction.IsolationLevel;
+            // #1 prepare sql
+            string sql = sqlTranslateService.PrepareGet(arg);
 
-            public void Commit()
+            // #2 get sql params
+            var sqlParam = new Dictionary<string, object>();
+            sqlParam[entityDescriptor.keyName] = keyValue;
+
+            // #3 execute
+            using var reader = ExecuteReader(sql: sql, param: (object)sqlParam);
+            if (reader.Read())
             {
-                originalTransaction.Commit();
-                TransactionState = ETransactionState.Committed;
+                var entity = (Entity)Activator.CreateInstance(typeof(Entity));
+                foreach (var column in entityDescriptor.allColumns)
+                {
+                    column.Set(entity, TypeUtil.ConvertToType(reader[column.name], column.type));
+                }
+                return entity;
             }
+            return default;
+
+        }
 
-            public void Dispose()
+
+        public override IQueryable<Entity> Query<Entity>()
+        {
+            var dbContextId = "SqlDbSet_" + GetHashCode();
+
+            Func<Expression, Type, object> QueryExecutor = (expression, type) =>
             {
-                originalTransaction.Dispose();
-                TransactionState = ETransactionState.Disposed;
-            }
+                // #1 convert to ExpressionNode
+                // (query) => query.Where().OrderBy().Skip().Take().Select().ToList();
+                // (users) => users.SelectMany(
+                //      user => users.Where(father => (father.id == user.fatherId)).DefaultIfEmpty(),
+                //      (user, father) => new <>f__AnonymousType4`2(user = user, father = father)
+                //  ).Where().Select();
+                var isArgument = QueryableBuilder.QueryTypeNameCompare(dbContextId);
+                ExpressionNode node = convertService.ConvertToData(expression, autoReduce: true, isArgument: isArgument);
+                //var strNode = Json.Serialize(node);
+
+
+                // #2 convert to Streams
+                // {select,left,joins,where,order,skip,take}
+                var stream = StreamReader.ReadNode(node);
+                //var strStream = Json.Serialize(stream);
+
+
+                // #3.1 ExecuteUpdate
+                if (stream is StreamToUpdate streamToUpdate)
+                {
+                    // get arg
+                    var resultEntityType = streamToUpdate.fieldsToUpdate.New_GetType();
+                    var arg = new QueryTranslateArgument(this, resultEntityType);
+
+                    (string sql, Dictionary<string, object> sqlParam) = sqlTranslateService.PrepareExecuteUpdate(arg, streamToUpdate);
+
+                    return Execute(sql: sql, param: (object)sqlParam);
+                }
+
+
+                // #3.3 Query
+                // #3.3.1
+                var combinedStream = stream as CombinedStream;
+                if (combinedStream == null) combinedStream = new CombinedStream("tmp") { source = stream };
+
+                // #3.3.2 execute and read result
+                switch (combinedStream.method)
+                {
+                    case nameof(Queryable_Extensions.ToExecuteString):
+                        {
+                            // ToExecuteString
+
+                            // get arg
+                            var arg = new QueryTranslateArgument(this, null);
+
+                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
+                            return sql;
+                        }
+                    case "Count":
+                        {
+                            // Count
+
+                            // get arg
+                            var arg = new QueryTranslateArgument(this, null);
+
+                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
+
+                            var count = ExecuteScalar(sql: sql, param: (object)sqlParam);
+                            return Convert.ToInt32(count);
+                        }
+                    case nameof(Queryable_Extensions.ExecuteDelete):
+                        {
+                            // ExecuteDelete
+
+                            // get arg
+                            var resultEntityType = (combinedStream.source as SourceStream)?.GetEntityType();
+                            var arg = new QueryTranslateArgument(this, resultEntityType);
+
+                            (string sql, Dictionary<string, object> sqlParam) = sqlTranslateService.PrepareExecuteDelete(arg, combinedStream);
+
+                            var count = Execute(sql: sql, param: (object)sqlParam);
+                            return count;
+                        }
+                    case "FirstOrDefault" or "First" or "LastOrDefault" or "Last":
+                        {
+                            // get arg
+                            var resultEntityType = expression.Type;
+                            var arg = new QueryTranslateArgument(this, resultEntityType);
+
+                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
+
+                            using var reader = ExecuteReader(sql: sql, param: (object)sqlParam);
+                            return dataReader.ReadData(reader);
+                        }
+                    case "ToList":
+                    case "":
+                    case null:
+                        {
+                            // ToList
+
+                            // get arg
+                            var resultEntityType = expression.Type.GetGenericArguments()?.FirstOrDefault();
+                            var arg = new QueryTranslateArgument(this, resultEntityType);
+
+                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
 
-            public void Rollback()
+                            using var reader = ExecuteReader(sql: sql, param: (object)sqlParam);
+                            return dataReader.ReadData(reader);
+                        }
+                }
+                throw new NotSupportedException("not supported query type: " + combinedStream.method);
+            };
+            return QueryableBuilder.Build<Entity>(QueryExecutor, dbContextId);
+
+        }
+
+        #endregion
+
+
+
+        #region #3 Update: Update UpdateRange
+
+        public override int Update<Entity>(Entity entity)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            // #1 prepare sql
+            (string sql, Func<object, Dictionary<string, object>> GetSqlParams) = sqlTranslateService.PrepareUpdate(arg);
+
+            // #2 get sql params
+            var sqlParam = GetSqlParams(entity);
+
+            // #3 execute
+            var affectedRowCount = Execute(sql: sql, param: (object)sqlParam);
+
+            return affectedRowCount;
+
+        }
+
+        public override int UpdateRange<Entity>(IEnumerable<Entity> entitys)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            // #1 prepare sql
+            (string sql, Func<object, Dictionary<string, object>> GetSqlParams) = sqlTranslateService.PrepareUpdate(arg);
+
+            // #2 execute
+            var affectedRowCount = 0;
+
+            foreach (var entity in entitys)
             {
-                originalTransaction.Rollback();
-                TransactionState = ETransactionState.RolledBack;
+                var sqlParam = GetSqlParams(entity);
+                affectedRowCount += Execute(sql: sql, param: (object)sqlParam);
             }
+            return affectedRowCount;
+        }
+
+        #endregion
+
+
+        #region #4 Delete : Delete DeleteRange DeleteByKey DeleteByKeys
+        public override int Delete<Entity>(Entity entity)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            var key = entityDescriptor.key.Get(entity);
+            return DeleteByKey<Entity>(key);
         }
 
+        public override int DeleteRange<Entity>(IEnumerable<Entity> entitys)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            var keys = entitys.Select(entity => entityDescriptor.key.Get(entity)).ToList();
+            return DeleteByKeys<Entity, object>(keys);
+        }
+
+
+        public override int DeleteByKey<Entity>(object keyValue)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            // #1 prepare sql
+            string sql = sqlTranslateService.PrepareDelete(arg);
+
+            // #2 get sql params
+            var sqlParam = new Dictionary<string, object>();
+            sqlParam[entityDescriptor.keyName] = keyValue;
+
+            // #3 execute
+            var affectedRowCount = Execute(sql: sql, param: (object)sqlParam);
+
+            return affectedRowCount;
+
+        }
+        public override int DeleteByKeys<Entity, Key>(IEnumerable<Key> keys)
+        {
+            // #0 get arg
+            var entityDescriptor = GetEntityDescriptor(typeof(Entity));
+            SqlTranslateArgument arg = new SqlTranslateArgument(this, entityDescriptor);
+
+            // #1 prepare sql
+            string sql = sqlTranslateService.PrepareDeleteRange(arg);
+
+            // #2 get sql params
+            var sqlParam = new Dictionary<string, object>();
+            sqlParam["keys"] = keys;
+
+            // #3 execute
+            var affectedRowCount = Execute(sql: sql, param: (object)sqlParam);
+
+            return affectedRowCount;
+        }
+
+        #endregion
+
+
+        #region Transaction
+        public virtual Func<SqlDbContext, ITransactionScope> createTransactionScope { set; get; }
+                    = (dbContext) => new SqlTransactionScope(dbContext);
+        protected virtual ITransactionScope transactionScope { get; set; }
+
+        public virtual IDbTransaction BeginTransaction()
+        {
+            transactionScope ??= createTransactionScope(this);
+            return transactionScope.BeginTransaction();
+        }
+        public virtual IDbTransaction GetCurrentTransaction() => transactionScope?.GetCurrentTransaction();
+
+        #endregion
+
 
 
         #region Execute

+ 13 - 239
src/Vit.Orm/Sql/SqlDbSet.cs

@@ -1,15 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-
-using Vit.Linq.ExpressionTree.ComponentModel;
-using System.Linq.Expressions;
 using Vit.Orm.Entity;
 using System.Reflection;
-using Vit.Linq;
-using Vit.Orm.Sql.DataReader;
-using Vit.Extensions.Linq_Extensions;
+using Vit.Orm.Sql.SqlTranslate;
 
 namespace Vit.Orm.Sql
 {
@@ -38,7 +32,7 @@ namespace Vit.Orm.Sql
         public override IEntityDescriptor entityDescriptor => _entityDescriptor;
 
 
-        public virtual ISqlTranslator sqlTranslator => dbContext.sqlTranslator;
+        public virtual ISqlTranslateService sqlTranslator => dbContext.sqlTranslateService;
 
         public SqlDbSet(SqlDbContext dbContext, IEntityDescriptor entityDescriptor)
         {
@@ -46,250 +40,30 @@ namespace Vit.Orm.Sql
             this._entityDescriptor = entityDescriptor;
         }
 
-        public override void Create()
-        {
-            string sql = sqlTranslator.PrepareCreate(entityDescriptor);
-
-            dbContext.Execute(sql: sql);
-        }
-
-
-
-
-        public override Entity Add(Entity entity)
-        {
-            // #1 prepare sql
-            (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) = sqlTranslator.PrepareAdd(this);
-
-            // #2 get sql params
-            var sqlParam = GetSqlParams(entity);
-
-            // #3 execute
-            var newKeyValue = dbContext.ExecuteScalar(sql: sql, param: (object)sqlParam);
-
-            if (newKeyValue != null)
-            {
-                var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
-                newKeyValue = TypeUtil.ConvertToUnderlyingType(newKeyValue, keyType);
-                entityDescriptor.key.Set(entity, newKeyValue);
-            }
-            return entity;
-        }
-
-        public override void AddRange(IEnumerable<Entity> entitys)
-        {
-            // #1 prepare sql
-            (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) = sqlTranslator.PrepareAdd(this);
-
-            // #2 execute
-            var affectedRowCount = 0;
-
-            var keyType = TypeUtil.GetUnderlyingType(entityDescriptor.key.type);
-            foreach (var entity in entitys)
-            {
-                var sqlParam = GetSqlParams(entity);
-                var newKeyValue = dbContext.ExecuteScalar(sql: sql, param: (object)sqlParam);
-                if (newKeyValue != null)
-                {
-                    newKeyValue = TypeUtil.ConvertToUnderlyingType(newKeyValue, keyType);
-                    entityDescriptor.key.Set(entity, newKeyValue);
-                }
-                affectedRowCount++;
-            }
-        }
-
-
-        public override Entity Get(object keyValue)
-        {
-            // #1 prepare sql
-            string sql = sqlTranslator.PrepareGet(this);
-
-            // #2 get sql params
-            var sqlParam = new Dictionary<string, object>();
-            sqlParam[entityDescriptor.keyName] = keyValue;
-
-            // #3 execute
-            using var reader = dbContext.ExecuteReader(sql: sql, param: (object)sqlParam);
-            if (reader.Read())
-            {
-                var entity = (Entity)Activator.CreateInstance(typeof(Entity));
-                foreach (var column in entityDescriptor.allColumns)
-                {
-                    column.Set(entity, TypeUtil.ConvertToType(reader[column.name], column.type));
-                }
-                return entity;
-            }
-            return default;
-
-
-        }
-
-
-        public override IQueryable<Entity> Query()
-        {
-            var dbContextId = "SqlDbSet_" + dbContext.GetHashCode();
-
-            Func<Expression, Type, object> QueryExecutor = (expression, type) =>
-            {
-                // #1 convert to ExpressionNode
-                // (query) => query.Where().OrderBy().Skip().Take().Select().ToList();
-                // (users) => users.SelectMany(
-                //      user => users.Where(father => (father.id == user.fatherId)).DefaultIfEmpty(),
-                //      (user, father) => new <>f__AnonymousType4`2(user = user, father = father)
-                //  ).Where().Select();
-                var isArgument = QueryableBuilder.QueryTypeNameCompare(dbContextId);
-                ExpressionNode node = dbContext.convertService.ConvertToData(expression, autoReduce: true, isArgument: isArgument);
-                //var strNode = Json.Serialize(node);
-
-
-                // #2 convert to Streams
-                // {select,left,joins,where,order,skip,take}
-                var stream = StreamReader.ReadNode(node);
-                //var strStream = Json.Serialize(stream);
-
-
-                // #3.1 ExecuteUpdate
-                if (stream is StreamToUpdate streamToUpdate)
-                {
-                    (string sql, Dictionary<string, object> sqlParam) = sqlTranslator.PrepareExecuteUpdate(streamToUpdate);
-
-                    return dbContext.Execute(sql: sql, param: (object)sqlParam);
-                }
-
-
-                // #3.3 Query
-                // #3.3.1
-                var combinedStream = stream as CombinedStream;
-                if (combinedStream == null) combinedStream = new CombinedStream("tmp") { source = stream };
-
-                // #3.3.2 execute and read result
-                switch (combinedStream.method)
-                {
-                    case nameof(Queryable_Extensions.ToExecuteString):
-                        {
-                            // ToExecuteString
-                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslator.PrepareQuery(combinedStream, entityType: null);
-                            return sql;
-                        }
-                    case "Count":
-                        {
-                            // Count
-                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslator.PrepareQuery(combinedStream, entityType: null);
-
-                            var count = dbContext.ExecuteScalar(sql: sql, param: (object)sqlParam);
-                            return Convert.ToInt32(count);
-                        }
-                    case nameof(Queryable_Extensions.ExecuteDelete):
-                        {
-                            // ExecuteDelete
-                            (string sql, Dictionary<string, object> sqlParam) = sqlTranslator.PrepareExecuteDelete(combinedStream);
-
-                            var count = dbContext.Execute(sql: sql, param: (object)sqlParam);
-                            return count;
-                        }
-                    case "FirstOrDefault" or "First" or "LastOrDefault" or "Last":
-                        {
-                            var entityType = expression.Type;
-                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslator.PrepareQuery(combinedStream, entityType);
-
-                            using var reader = dbContext.ExecuteReader(sql: sql, param: (object)sqlParam);
-                            return dataReader.ReadData(reader);
-                        }
-                    case "ToList":
-                    case "":
-                    case null:
-                        {
-                            // ToList
-                            var entityType = expression.Type.GetGenericArguments()?.FirstOrDefault();
-                            (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslator.PrepareQuery(combinedStream, entityType);
-
-                            using var reader = dbContext.ExecuteReader(sql: sql, param: (object)sqlParam);
-                            return dataReader.ReadData(reader);
-                        }
-                }
-                throw new NotSupportedException("not supported query type: " + combinedStream.method);
-            };
-            return QueryableBuilder.Build<Entity>(QueryExecutor, dbContextId);
-        }
-
+        public override void Create() => dbContext.Create<Entity>();
 
 
 
+        public override Entity Add(Entity entity) => dbContext.Add(entity);
+        public override void AddRange(IEnumerable<Entity> entitys) => dbContext.AddRange(entitys);
 
-        public override int Update(Entity entity)
-        {
-            // #1 prepare sql
-            (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) = sqlTranslator.PrepareUpdate(this);
 
-            // #2 get sql params
-            var sqlParam = GetSqlParams(entity);
 
-            // #3 execute
-            var affectedRowCount = dbContext.Execute(sql: sql, param: (object)sqlParam);
 
-            return affectedRowCount;
-        }
+        public override Entity Get(object keyValue) => dbContext.Get<Entity>(keyValue);
+        public override IQueryable<Entity> Query() => dbContext.Query<Entity>();
 
-        public override int UpdateRange(IEnumerable<Entity> entitys)
-        {
-            // #1 prepare sql
-            (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) = sqlTranslator.PrepareUpdate(this);
 
-            // #2 execute
-            var affectedRowCount = 0;
 
-            foreach (var entity in entitys)
-            {
-                var sqlParam = GetSqlParams(entity);
-                affectedRowCount += dbContext.Execute(sql: sql, param: (object)sqlParam);
-            }
-            return affectedRowCount;
-        }
+        public override int Update(Entity entity) => dbContext.Update<Entity>(entity);
+        public override int UpdateRange(IEnumerable<Entity> entitys) => dbContext.UpdateRange<Entity>(entitys);
 
 
 
-        public override int Delete(Entity entity)
-        {
-            var key = entityDescriptor.key.Get(entity);
-            return DeleteByKey(key);
-        }
-
-        public override int DeleteRange(IEnumerable<Entity> entitys)
-        {
-            var keys = entitys.Select(entity => entityDescriptor.key.Get(entity)).ToList();
-            return DeleteByKeys(keys);
-        }
-
-
-        public override int DeleteByKey(object keyValue)
-        {
-            // #1 prepare sql
-            string sql = sqlTranslator.PrepareDelete(this);
-
-            // #2 get sql params
-            var sqlParam = new Dictionary<string, object>();
-            sqlParam[entityDescriptor.keyName] = keyValue;
-
-            // #3 execute
-            var affectedRowCount = dbContext.Execute(sql: sql, param: (object)sqlParam);
-
-            return affectedRowCount;
-        }
-
-        public override int DeleteByKeys<Key>(IEnumerable<Key> keys)
-        {
-            // #1 prepare sql
-            string sql = sqlTranslator.PrepareDeleteRange(this);
-
-            // #2 get sql params
-            var sqlParam = new Dictionary<string, object>();
-            sqlParam["keys"] = keys;
-
-            // #3 execute
-            var affectedRowCount = dbContext.Execute(sql: sql, param: (object)sqlParam);
-
-            return affectedRowCount;
-        }
+        public override int Delete(Entity entity) => dbContext.Delete<Entity>(entity);
+        public override int DeleteRange(IEnumerable<Entity> entitys) => dbContext.DeleteRange<Entity>(entitys);
+        public override int DeleteByKey(object keyValue) => dbContext.DeleteByKey<Entity>(keyValue);
+        public override int DeleteByKeys<Key>(IEnumerable<Key> keys) => dbContext.DeleteByKeys<Entity, Key>(keys);
 
     }
 }

+ 220 - 0
src/Vit.Orm/Sql/SqlTranslate/BaseQueryTranslateService.cs

@@ -0,0 +1,220 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Sql.DataReader;
+
+namespace Vit.Orm.Sql.SqlTranslate
+{
+
+    public abstract class BaseQueryTranslateService : IQueryTranslateService
+    {
+        public SqlTranslateService sqlTranslator { get; protected set; }
+
+
+        public BaseQueryTranslateService(SqlTranslateService sqlTranslator)
+        {
+            this.sqlTranslator = sqlTranslator;
+        }
+
+
+
+        public virtual string BuildQuery(QueryTranslateArgument arg, CombinedStream stream)
+        {
+
+            string sql = "";
+
+            // #0  select
+            sql += ReadSelect(arg, stream);
+
+
+            #region #1 from
+            sql += "\r\n from " + ReadInnerTable(arg, stream.source);
+            #endregion
+
+            #region #2 join
+            if (stream.joins != null)
+            {
+                sql +=  ReadJoin(arg,stream);
+            }
+            #endregion
+
+            // #3 where 1=1
+            if (stream.where != null)
+            {
+                var where = sqlTranslator.EvalExpression(arg, stream.where);
+                if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n where " + where;
+            }
+
+            #region #4 group by
+            if (stream.groupByFields != null)
+            {
+                sql += "\r\n group by " + ReadGroupBy(arg, stream);
+            }
+            #endregion
+
+            #region #5 having
+            if (stream.having != null)
+            {
+                var where = sqlTranslator.EvalExpression(arg, stream.having);
+                if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n having " + where;
+            }
+            #endregion
+
+
+            // #6 OrderBy
+            if (stream.orders?.Any() == true)
+            {
+                sql += "\r\n order by " + ReadOrderBy(arg, stream);
+            }
+
+            // #7 Range,  limit 1000,10       limit {skip},{take}   |     limit {take}
+            if (stream.take != null || stream.skip != null)
+            {
+                string sqlRange = "limit " + (stream.skip == null ? "" : (stream.skip + ",")) + (stream.take ?? 100000000);
+                sql += "\r\n " + sqlRange;
+            }
+
+            return sql;
+        }
+
+
+        #region Read partial query
+
+
+        /// <summary>
+        /// "select *";
+        /// </summary>
+        /// <param name="stream"></param>
+        /// <returns></returns>
+        protected virtual string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            return prefix + " *";
+        }
+
+        protected virtual string ReadJoin(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var sql = "";
+            stream.joins?.ForEach(streamToJoin =>
+            {
+                sql += "\r\n " + (streamToJoin.joinType == EJoinType.InnerJoin ? "inner join" : "left join");
+                sql += " " + ReadInnerTable(arg, streamToJoin.right);
+
+                var on = sqlTranslator.EvalExpression(arg, streamToJoin.on);
+                if (!string.IsNullOrWhiteSpace(on)) sql += " on " + on;
+            });
+            return sql;
+        }
+        protected virtual string ReadGroupBy(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            var node = stream.groupByFields;
+            List<string> fields = new();
+            if (node?.nodeType == NodeType.New)
+            {
+                ExpressionNode_New newNode = node;
+                newNode.constructorArgs.ForEach((Action<MemberBind>)(nodeArg =>
+                {
+                    fields.Add(sqlTranslator.EvalExpression(arg, (ExpressionNode)nodeArg.value));
+                }));
+            }
+            else if (node?.nodeType == NodeType.Member)
+            {
+                fields.Add(sqlTranslator.EvalExpression(arg, node));
+            }
+            else
+            {
+                throw new NotSupportedException("[QueryTranslator] groupByFields is not valid: must be New or Member");
+            }
+           return String.Join(", ", fields);
+        }
+        protected virtual string ReadOrderBy(QueryTranslateArgument arg, CombinedStream stream) 
+        {
+            var fields = stream.orders.Select(field =>
+                {
+                    var sqlField = sqlTranslator.EvalExpression(arg, field.member);
+                    return sqlField + " " + (field.asc ? "asc" : "desc");
+                }
+            ).ToList();
+
+            return String.Join(", ", fields);
+        }
+        #endregion
+
+        protected string ReadInnerTable(QueryTranslateArgument arg, IStream stream)
+        {
+            if (stream is SourceStream sourceStream)
+            {
+                IQueryable query = sourceStream.GetSource() as IQueryable;
+                var tableName = arg.dbContext.GetEntityDescriptor(query.ElementType)?.tableName;
+                return $"{sqlTranslator.DelimitIdentifier(tableName)} as " + stream.alias;
+            }
+            if (stream is CombinedStream baseStream)
+            {
+                var innerQuery = BuildQuery(arg, baseStream);
+                return $"({innerQuery}) as " + stream.alias;
+            }
+            throw new NotSupportedException();
+        }
+        protected virtual string BuildReader(QueryTranslateArgument arg, CombinedStream stream, EntityReader reader)
+        {
+            var resultEntityType = arg.resultEntityType;
+            ExpressionNode selectedFields = stream.select?.fields as ExpressionNode;
+            if (selectedFields == null)
+            {
+                if (stream.joins?.Any() != true && resultEntityType != null)
+                {
+                    selectedFields = ExpressionNode.Member(parameterName: stream.source.alias, memberName: null).Member_SetType(resultEntityType);
+                }
+            }
+
+            if (selectedFields == null)
+                throw new NotSupportedException("select could not be null");
+
+            if (resultEntityType == null && selectedFields.nodeType == NodeType.New)
+            {
+                resultEntityType = selectedFields.New_GetType();
+            }
+
+            //if (resultEntityType == null)
+            //    throw new NotSupportedException("resultEntityType could not be null");
+
+            var sqlFields = reader.BuildSelect(arg, resultEntityType, sqlTranslator, arg.dbContext.convertService, selectedFields);
+            if (arg.dataReader == null) arg.dataReader = reader;
+            return (stream.distinct == true ? "distinct " : "") + sqlFields;
+        }
+
+        protected virtual void ReverseOrder(QueryTranslateArgument arg, CombinedStream stream)
+        {
+            stream.orders ??= new();
+            var orders = stream.orders;
+            // make sure orders exist
+            if (!orders.Any())
+            {
+                AddOrder(stream.source);
+                stream.joins?.ForEach(right => AddOrder(right.right));
+
+                #region AddOrder
+                void AddOrder(IStream source)
+                {
+                    if (source is SourceStream sourceStream)
+                    {
+                        var entityType = sourceStream.GetEntityType();
+                        var entityDescriptor = arg.dbContext.GetEntityDescriptor(entityType);
+                        if (entityDescriptor != null)
+                        {
+                            var member = ExpressionNode_RenameableMember.Member(stream: source, entityType);
+                            member.memberName = entityDescriptor.keyName;
+                            orders.Add(new OrderField { member = member, asc = true });
+                        }
+                    }
+                }
+                #endregion
+            }
+
+            // reverse order
+            orders?.ForEach(order => order.asc = !order.asc);
+        }
+    }
+
+}

+ 10 - 0
src/Vit.Orm/Sql/SqlTranslate/IQueryTranslateService.cs

@@ -0,0 +1,10 @@
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+
+namespace Vit.Orm.Sql.SqlTranslate
+{
+    public interface IQueryTranslateService 
+    {
+        string BuildQuery(QueryTranslateArgument arg, CombinedStream stream);
+    }
+
+}

+ 18 - 22
src/Vit.Orm/Sql/ISqlTranslator.cs → src/Vit.Orm/Sql/SqlTranslate/ISqlTranslateService.cs

@@ -5,11 +5,10 @@ using Vit.Linq.ExpressionTree.ComponentModel;
 using Vit.Linq.ExpressionTree.CollectionsQuery;
 using Vit.Orm.Entity;
 
-namespace Vit.Orm.Sql
+namespace Vit.Orm.Sql.SqlTranslate
 {
-    public interface ISqlTranslator
+    public interface ISqlTranslateService
     {
-        IEntityDescriptor GetEntityDescriptor(Type entityType);
 
         /// <summary>
         ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
@@ -27,40 +26,37 @@ namespace Vit.Orm.Sql
         ///     A valid name based on the candidate name.
         /// </returns>
         string GenerateParameterName(string name);
-        string GetTableName(Type entityType);
         string GetSqlField(string tableName, string columnName);
-        string GetSqlField(ExpressionNode_Member member);
+        string GetSqlField(ExpressionNode_Member member, DbContext dbContext);
 
+        string EvalExpression(QueryTranslateArgument arg, ExpressionNode data);
 
-        /// <summary>
-        /// functionName example:  Count, Max, Min, Sum, Average
-        /// </summary>
-        /// <param name="functionName"></param>
-        /// <param name="tableName"></param>
-        /// <param name="columnName"></param>
-        /// <returns></returns>
-        string GetSqlField_Aggregate(string functionName, string tableName, string columnName);
+        // #0 Schema :  PrepareCreate
+        string PrepareCreate(IEntityDescriptor entityDescriptor);
 
 
+        // #1 Create :  PrepareAdd
 
-        string PrepareCreate(IEntityDescriptor entityDescriptor);
+        (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg);
 
-        string PrepareGet<Entity>(DbSet<Entity> dbSet);
 
-        (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(CombinedStream combinedStream, Type entityType);
+        // #2 Retrieve : PrepareGet PrepareQuery
+        string PrepareGet(SqlTranslateArgument arg);
+        (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg,CombinedStream combinedStream);
 
 
-        (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) PrepareAdd<Entity>(DbSet<Entity> dbSet);
 
+        // #3 Update: PrepareUpdate PrepareExecuteUpdate
+        (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareUpdate(SqlTranslateArgument arg);
+        (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream);
 
-        (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) PrepareUpdate<Entity>(DbSet<Entity> dbSet);
-        (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(CombinedStream combinedStream);
 
-        string PrepareDelete<Entity>(DbSet<Entity> dbSet);
+        // #4 Delete: PrepareDelete PrepareDeleteRange PrepareExecuteDelete
+        string PrepareDelete(SqlTranslateArgument arg);
 
-        string PrepareDeleteRange<Entity>(DbSet<Entity> dbSet);
+        string PrepareDeleteRange(SqlTranslateArgument arg);
 
-        (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(CombinedStream combinedStream);
+        (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg,CombinedStream combinedStream);
 
 
 

+ 30 - 0
src/Vit.Orm/Sql/SqlTranslate/QueryTranslateArgument.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Vit.Orm.Entity;
+
+namespace Vit.Orm.Sql.SqlTranslate
+{
+    public class QueryTranslateArgument
+    {
+        public DbContext dbContext { get; protected set; }
+ 
+        public Type resultEntityType { get; protected set; }
+
+
+        public QueryTranslateArgument(DbContext dbContext, Type resultEntityType)
+        {
+            this.dbContext = dbContext;
+            this.resultEntityType = resultEntityType;
+        }
+
+
+
+        public IDbDataReader dataReader { get; set; }
+        public Dictionary<string, object> sqlParam { get; protected set; } = new Dictionary<string, object>();
+
+        protected int paramIndex = 0;
+        public string NewParamName() => "param" + (paramIndex++);
+    }
+}

+ 66 - 0
src/Vit.Orm/Sql/SqlTranslate/QueryTranslateService.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.DataReader;
+using Vit.Orm.Sql.DataReader;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Extensions.Linq_Extensions;
+using System.IO;
+
+
+namespace Vit.Orm.Sql.SqlTranslate
+{
+    public class QueryTranslateService : BaseQueryTranslateService
+    {
+        /* //sql
+        select u.id, u.name, u.birth ,u.fatherId ,u.motherId,    father.name,  mother.name
+        from User u
+        inner join User father on u.fatherId = father.id 
+        left join User mother on u.motherId = mother.id
+        where u.id > 1
+        limit 1,5;
+         */
+
+
+
+
+        public QueryTranslateService(SqlTranslateService sqlTranslator) : base(sqlTranslator)
+        {
+        }
+
+
+        protected override string ReadSelect(QueryTranslateArgument arg, CombinedStream stream, string prefix = "select")
+        {
+            switch (stream.method)
+            {
+                case "Count":
+                    {
+                        var reader = new NumScalarReader();
+                        if (arg.dataReader == null) arg.dataReader = reader;
+                        return prefix + " " + "count(*)";
+                    }
+                case "" or null or "ToList" or nameof(Queryable_Extensions.ToExecuteString):
+                    {
+                        var reader = new EntityReader();
+                        return prefix + " " + BuildReader(arg, stream, reader);
+                    }
+                case "FirstOrDefault" or "First" or "LastOrDefault" or "Last":
+                    {
+                        stream.take = 1;
+                        stream.skip = null;
+
+                        if (stream.method.Contains("Last"))
+                            ReverseOrder(arg, stream);
+
+                        var nullable = stream.method.Contains("OrDefault");
+                        var reader = new FirstEntityReader { nullable = nullable };
+                        return prefix + " " + BuildReader(arg,stream,reader);
+                    }
+            }
+            throw new NotSupportedException("not supported method: " + stream.method);
+        }
+     
+
+
+    }
+}

+ 20 - 0
src/Vit.Orm/Sql/SqlTranslate/SqlTranslateArgument.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Vit.Orm.Entity;
+
+namespace Vit.Orm.Sql.SqlTranslate
+{
+    public class SqlTranslateArgument
+    {
+        public DbContext dbContext { get; protected set; }
+        public IEntityDescriptor entityDescriptor { get; protected set; }
+
+        public SqlTranslateArgument(DbContext dbContext, IEntityDescriptor entityDescriptor)
+        {
+            this.dbContext = dbContext;
+            this.entityDescriptor = entityDescriptor;
+        }
+    }
+}

+ 387 - 0
src/Vit.Orm/Sql/SqlTranslate/SqlTranslateService.cs

@@ -0,0 +1,387 @@
+using System;
+using System.Collections.Generic;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.CollectionsQuery;
+using Vit.Orm.Entity;
+using System.Linq;
+using System.Linq.Expressions;
+using Vit.Linq.ExpressionTree.ExpressionConvertor;
+
+namespace Vit.Orm.Sql.SqlTranslate
+{
+    public abstract class SqlTranslateService : ISqlTranslateService
+    {
+        public SqlTranslateService()
+        {
+        }
+
+
+
+        #region DelimitIdentifier
+        /// <summary>
+        ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to delimit.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public virtual string DelimitIdentifier(string identifier) => $"\"{EscapeIdentifier(identifier)}\""; // Interpolation okay; strings
+
+        /// <summary>
+        ///     Generates the escaped SQL representation of an identifier (column name, table name, etc.).
+        /// </summary>
+        /// <param name="identifier">The identifier to be escaped.</param>
+        /// <returns>
+        ///     The generated string.
+        /// </returns>
+        public virtual string EscapeIdentifier(string identifier) => identifier.Replace("\"", "\"\"");
+
+        /// <summary>
+        ///     Generates a valid parameter name for the given candidate name.
+        /// </summary>
+        /// <param name="name">The candidate name for the parameter.</param>
+        /// <returns>
+        ///     A valid name based on the candidate name.
+        /// </returns>
+        public virtual string GenerateParameterName(string name) => name.StartsWith("@", StringComparison.Ordinal) ? name : "@" + name;
+        #endregion
+
+
+
+        public virtual string GetSqlField(string tableName, string columnName)
+        {
+            return $"{DelimitIdentifier(tableName)}.{DelimitIdentifier(columnName)}";
+        }
+
+        /// <summary>
+        /// user.id
+        /// </summary>
+        /// <param name="member"></param>
+        /// <param name="dbContext"></param>
+        /// <returns></returns>
+        public virtual string GetSqlField(ExpressionNode_Member member, DbContext dbContext)
+        {
+            var memberName = member.memberName;
+            if (string.IsNullOrWhiteSpace(memberName))
+            {
+                var entityType = member.Member_GetType();
+                memberName = dbContext.GetEntityDescriptor(entityType)?.keyName;
+            }
+
+            // 1: {"nodeType":"Member","parameterName":"a0","memberName":"id"}
+            // 2: {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
+            return GetSqlField(member.objectValue?.parameterName ?? member.parameterName, memberName);
+        }
+
+        protected abstract string GetDbType(Type type);
+
+
+        #region EvalExpression
+
+        /// <summary>
+        /// read where or value or on
+        /// </summary>
+        /// <param name="arg"></param>
+        /// <returns></returns>
+        /// <exception cref="NotSupportedException"></exception>
+        /// <param name="data"></param>
+        public virtual string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
+        {
+            switch (data.nodeType)
+            {
+                case NodeType.And:
+                    ExpressionNode_And and = data;
+                    return $"({EvalExpression(arg, and.left)}) and ({EvalExpression(arg, and.right)})";
+
+                case NodeType.Or:
+                    ExpressionNode_Or or = data;
+                    return $"({EvalExpression(arg, or.left)}) or ({EvalExpression(arg, or.right)})";
+
+                case NodeType.Not:
+                    ExpressionNode_Not not = data;
+                    return $"not ({EvalExpression(arg, not.body)})";
+
+                case NodeType.ArrayIndex:
+                    throw new NotSupportedException(data.nodeType);
+                //ExpressionNode_ArrayIndex arrayIndex = data;
+                //return Expression.ArrayIndex(ToExpression(arg, arrayIndex.left), ToExpression(arg, arrayIndex.right));
+                case NodeType.Equal:
+                case NodeType.NotEqual:
+                    {
+                        ExpressionNode_Binary binary = data;
+
+                        //   "= null"  ->   "is null" ,    "!=null" -> "is not null"   
+                        if (binary.right.nodeType == NodeType.Constant && binary.right.value == null)
+                        {
+                            var opera = data.nodeType == NodeType.Equal ? "is null" : "is not null";
+                            return $"{EvalExpression(arg, binary.left)} " + opera;
+                        }
+                        else if (binary.left.nodeType == NodeType.Constant && binary.left.value == null)
+                        {
+                            var opera = data.nodeType == NodeType.Equal ? "is null" : "is not null";
+                            return $"{EvalExpression(arg, binary.right)} " + opera;
+                        }
+
+                        var @operator = operatorMap[data.nodeType];
+                        return $"{EvalExpression(arg, binary.left)} {@operator} {EvalExpression(arg, binary.right)}";
+                    }
+                case NodeType.LessThan:
+                case NodeType.LessThanOrEqual:
+                case NodeType.GreaterThan:
+                case NodeType.GreaterThanOrEqual:
+                    {
+                        ExpressionNode_Binary binary = data;
+                        var @operator = operatorMap[data.nodeType];
+                        return $"{EvalExpression(arg, binary.left)} {@operator} {EvalExpression(arg, binary.right)}";
+                    }
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall methodCall = data;
+
+                        switch (methodCall.methodName)
+                        {
+                            // ##1 in
+                            case nameof(Enumerable.Contains):
+                                {
+                                    var values = methodCall.arguments[0];
+                                    var member = methodCall.arguments[1];
+                                    return $"{EvalExpression(arg, member)} in {EvalExpression(arg, values)}";
+                                }
+
+                            // ##2 db primitive function
+                            case nameof(DbFunction.Call):
+                                {
+                                    var functionName = methodCall.arguments[0].value as string;
+                                    var argList = methodCall.arguments.AsQueryable().Skip(1).Select(argNode => EvalExpression(arg, argNode)).ToList();
+                                    var funcArgs = string.Join(",", argList);
+                                    return $"{functionName}({funcArgs})";
+                                }
+
+
+                            #region ##3 Aggregate
+                            case nameof(Enumerable.Count) when methodCall.arguments.Length == 1:
+                                {
+                                    var stream = methodCall.arguments[0] as ExpressionNode_Member;
+                                    //if (stream?.nodeType != NodeType.Member) break;
+                                    return "Count(*)";
+                                }
+                            case nameof(Enumerable.Max) or nameof(Enumerable.Min) or nameof(Enumerable.Sum) or nameof(Enumerable.Average) when methodCall.arguments.Length == 2:
+                                {
+                                    var stream = methodCall.arguments[0] as ExpressionNode_Member;
+                                    if (stream?.nodeType != NodeType.Member) break;
+
+
+                                    var lambdaFieldSelect = methodCall.arguments[1] as ExpressionNode_Lambda;
+
+                                    var parameterName = lambdaFieldSelect.parameterNames[0];
+                                    var parameterValue = (ExpressionNode)stream;
+                                    Func<ExpressionNode_Member, ExpressionNode> GetParameter = (member) =>
+                                    {
+                                        if (member.nodeType == NodeType.Member && member.parameterName == parameterName)
+                                        {
+                                            if (string.IsNullOrWhiteSpace(member.memberName))
+                                            {
+                                                return parameterValue;
+                                            }
+                                            else
+                                            {
+                                                return ExpressionNode.Member(objectValue: parameterValue, memberName: member.memberName).Member_SetType(member.Member_GetType());
+                                            }
+                                        }
+                                        return default;
+                                    };
+
+                                    var body = StreamReader.DeepClone(lambdaFieldSelect.body, GetParameter);
+                                    var funcName = methodCall.methodName;
+                                    if (funcName == nameof(Enumerable.Average)) funcName = "AVG";
+
+                                    return $"{funcName}({EvalExpression(arg, body)})";
+                                }
+                                #endregion
+                        }
+                        throw new NotSupportedException("[QueryTranslator] not suported MethodCall: " + methodCall.methodName);
+                    }
+
+
+                #region Read Value
+
+                case NodeType.Member:
+                    return GetSqlField(data, arg.dbContext);
+
+                case NodeType.Constant:
+                    ExpressionNode_Constant constant = data;
+                    var paramName = arg.NewParamName();
+                    arg.sqlParam[paramName] = constant.value;
+                    return "@" + paramName;
+
+                    #endregion
+            }
+            throw new NotSupportedException("[QueryTranslator] not suported nodeType: " + data.nodeType);
+        }
+
+
+        protected readonly static Dictionary<string, string> operatorMap = new Dictionary<string, string>
+        {
+            [NodeType.Equal] = "=",
+            [NodeType.NotEqual] = "!=",
+            [NodeType.LessThan] = "<",
+            [NodeType.LessThanOrEqual] = "<=",
+            [NodeType.GreaterThan] = ">",
+            [NodeType.GreaterThanOrEqual] = ">=",
+        };
+        #endregion
+
+
+        // #0 Schema :  PrepareCreate
+        public abstract string PrepareCreate(IEntityDescriptor entityDescriptor);
+
+
+        #region #1 Create :  PrepareAdd
+        public virtual (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg)
+        {
+            /* //sql
+             insert into user(name,birth,fatherId,motherId) values('','','');
+             select seq from sqlite_sequence where name='user';
+              */
+            var entityDescriptor = arg.entityDescriptor;
+
+            var columns = entityDescriptor.columns;
+
+            // #1 GetSqlParams 
+            Func<object, Dictionary<string, object>> GetSqlParams = (entity) =>
+            {
+                var sqlParam = new Dictionary<string, object>();
+                foreach (var column in columns)
+                {
+                    var columnName = column.name;
+                    var value = column.Get(entity);
+
+                    sqlParam[columnName] = value;
+                }
+                return sqlParam;
+            };
+
+            #region #2 columns 
+            List<string> columnNames = new List<string>();
+            List<string> valueParams = new List<string>();
+            string columnName;
+
+            foreach (var column in columns)
+            {
+                columnName = column.name;
+
+                columnNames.Add(DelimitIdentifier(columnName));
+                valueParams.Add(GenerateParameterName(columnName));
+            }
+            #endregion
+
+            // #3 build sql
+            string sql = $@"insert into {DelimitIdentifier(entityDescriptor.tableName)}({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
+            //sql+=$"select seq from sqlite_sequence where name = '{tableName}'; ";
+
+            return (sql, GetSqlParams);
+        }
+        #endregion
+
+
+        #region #2 Retrieve : PrepareGet PrepareQuery
+        public virtual string PrepareGet(SqlTranslateArgument arg)
+        {
+            var entityDescriptor = arg.entityDescriptor;
+
+            // #2 build sql
+            string sql = $@"select * from {DelimitIdentifier(entityDescriptor.tableName)} where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)};";
+
+            return sql;
+        }
+
+        public abstract (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg, CombinedStream combinedStream);
+        #endregion
+
+
+
+        #region #3 Update: PrepareUpdate PrepareExecuteUpdate
+        public virtual (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareUpdate(SqlTranslateArgument arg)
+        {
+            /* //sql
+                update user set name='' where id=7;
+            */
+
+            var entityDescriptor = arg.entityDescriptor;
+            var sqlParam = new Dictionary<string, object>();
+
+            // #1 GetSqlParams
+            Func<object, Dictionary<string, object>> GetSqlParams = (entity) =>
+            {
+                var sqlParam = new Dictionary<string, object>();
+                foreach (var column in entityDescriptor.allColumns)
+                {
+                    var columnName = column.name;
+                    var value = column.Get(entity);
+
+                    sqlParam[columnName] = value;
+                }
+                //sqlParam[entityDescriptor.keyName] = entityDescriptor.key.Get(entity);
+                return sqlParam;
+            };
+
+            // #2 columns
+            List<string> columnsToUpdate = new List<string>();
+            string columnName;
+            foreach (var column in entityDescriptor.columns)
+            {
+                columnName = column.name;
+                columnsToUpdate.Add($"{DelimitIdentifier(columnName)}={GenerateParameterName(columnName)}");
+            }
+
+            // #3 build sql
+            string sql = $@"update {DelimitIdentifier(entityDescriptor.tableName)} set {string.Join(",", columnsToUpdate)} where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)};";
+
+            return (sql, GetSqlParams);
+        }
+
+        public abstract (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream);
+
+        #endregion
+
+
+        #region #4 Delete: PrepareDelete PrepareDeleteRange PrepareExecuteDelete
+
+        public virtual string PrepareDelete(SqlTranslateArgument arg)
+        {
+            /* //sql
+            delete from user where id = 7;
+            */
+            var entityDescriptor = arg.entityDescriptor;
+
+            // #2 build sql
+            string sql = $@"delete from {DelimitIdentifier(entityDescriptor.tableName)} where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)};";
+
+            return sql;
+        }
+
+
+        public virtual string PrepareDeleteRange(SqlTranslateArgument arg)
+        {
+            /* //sql
+            delete from user where id in ( 7 ) ;
+            */
+            var entityDescriptor = arg.entityDescriptor;
+
+            // #2 build sql
+            string sql = $@"delete from {DelimitIdentifier(entityDescriptor.tableName)} where {DelimitIdentifier(entityDescriptor.keyName)} in {GenerateParameterName("keys")};";
+
+            return sql;
+        }
+
+
+        public abstract (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg, CombinedStream combinedStream);
+
+
+        #endregion
+
+
+
+    }
+}

+ 40 - 0
src/Vit.Orm/Sql/Transaction/DbTransactionWrap.cs

@@ -0,0 +1,40 @@
+using System.Data;
+
+namespace Vit.Orm.Sql.Transaction
+{
+    public class DbTransactionWrap : IDbTransaction
+    {
+        public enum ETransactionState
+        {
+            Active, Committed, RolledBack, Disposed
+        }
+        public virtual ETransactionState TransactionState { get; protected set; } = ETransactionState.Active;
+        public DbTransactionWrap(IDbTransaction transaction)
+        {
+            originalTransaction = transaction;
+        }
+        public IDbTransaction originalTransaction;
+
+        public virtual IDbConnection Connection => originalTransaction.Connection;
+
+        public virtual System.Data.IsolationLevel IsolationLevel => originalTransaction.IsolationLevel;
+
+        public virtual void Commit()
+        {
+            originalTransaction.Commit();
+            TransactionState = ETransactionState.Committed;
+        }
+        public virtual void Dispose()
+        {
+            originalTransaction.Dispose();
+            TransactionState = ETransactionState.Disposed;
+        }
+
+        public virtual void Rollback()
+        {
+            originalTransaction.Rollback();
+            TransactionState = ETransactionState.RolledBack;
+        }
+    }
+
+}

+ 11 - 0
src/Vit.Orm/Sql/Transaction/ITransactionScope.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Data;
+
+namespace Vit.Orm.Sql.Transaction
+{
+    public interface ITransactionScope : IDisposable
+    {
+        IDbTransaction BeginTransaction();
+        IDbTransaction GetCurrentTransaction();
+    }
+}

+ 55 - 0
src/Vit.Orm/Sql/Transaction/SqlTransactionScope.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Text;
+
+namespace Vit.Orm.Sql.Transaction
+{
+    public class SqlTransactionScope : ITransactionScope
+    {
+        public SqlTransactionScope(SqlDbContext dbContext)
+        {
+            this.dbContext = dbContext;
+        }
+
+        protected SqlDbContext dbContext;
+        protected Stack<DbTransactionWrap> transactions = new();
+
+
+        public virtual IDbTransaction BeginTransaction()
+        {
+            var dbConnection = dbContext.dbConnection;
+            if (dbConnection.State != ConnectionState.Open) dbConnection.Open();
+            var transaction = dbConnection.BeginTransaction();
+
+            var transactionWrap = new DbTransactionWrap(transaction);
+            transactions.Push(transactionWrap);
+            return transactionWrap;
+        }
+
+        public virtual void Dispose()
+        {
+            while (transactions?.Count > 0)
+            {
+                var transaction = transactions.Pop();
+                if (transaction?.TransactionState != DbTransactionWrap.ETransactionState.Disposed)
+                {
+                    transaction?.Dispose();
+                }
+            }
+            transactions = null;
+        }
+
+        public virtual IDbTransaction GetCurrentTransaction()
+        {
+            while (transactions?.Count > 0)
+            {
+                var tran = transactions.Peek();
+                if (tran?.TransactionState == DbTransactionWrap.ETransactionState.Active) return tran.originalTransaction;
+                transactions.Pop();
+            }
+            return null;
+        }
+    }
+
+}

+ 0 - 354
src/Vit.Orm/Sql/Translator/BaseQueryTranslator.cs

@@ -1,354 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Orm.Sql;
-using System.Linq.Expressions;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-
-namespace Vit.Orm.Sql.Translator
-{
-    public abstract class BaseQueryTranslator
-    {
-        public SqlTranslator sqlTranslator { get; protected set; }
-
-
-        public BaseQueryTranslator(SqlTranslator sqlTranslator)
-        {
-            this.sqlTranslator = sqlTranslator;
-        }
-
-
-
-        public IDbDataReader dataReader;
-        public Dictionary<string, object> sqlParam { get; protected set; } = new Dictionary<string, object>();
-
-        protected int paramIndex = 0;
-        protected string NewParamName() => "param" + (paramIndex++);
-
-
-        /// <summary>
-        /// return "*";
-        /// </summary>
-        /// <param name="stream"></param>
-        /// <returns></returns>
-        protected virtual string ReadSelect(CombinedStream stream)
-        {
-            return "*";
-        }
-
-        public virtual string BuildQuery(CombinedStream stream)
-        {
-
-            string sql = "";
-
-            // #0  select
-            sql += "select " + ReadSelect(stream);
-
-
-            #region #1 source
-            // from User u
-            sql += "\r\n from " + ReadInnerTable(stream.source);
-            #endregion
-
-            #region #2 join
-            {
-                stream.joins?.ForEach(streamToJoin =>
-                {
-                    sql += "\r\n " + (streamToJoin.joinType == EJoinType.InnerJoin ? "inner join" : "left join");
-                    sql += " " + ReadInnerTable(streamToJoin.right);
-
-                    var where = ReadEval(streamToJoin.on);
-                    if (!string.IsNullOrWhiteSpace(where)) sql += " on " + where;
-                });
-            }
-            #endregion
-
-            // #3 where 1=1
-            if (stream.where != null)
-            {
-                var where = ReadEval(stream.where);
-                if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n where " + where;
-            }
-
-            // #4 GROUP BY xxxx  Having XXX
-            if (stream.isGroupedStream)
-            {
-                #region ##1 group by
-                var node = stream.groupByFields;
-                List<string> fields = new();
-                if (node?.nodeType == NodeType.New)
-                {
-                    ExpressionNode_New newNode = node;
-                    newNode.constructorArgs.ForEach((Action<MemberBind>)(arg =>
-                    {
-                        fields.Add(this.ReadEval((ExpressionNode)arg.value));
-                    }));
-                }
-                else if (node?.nodeType == NodeType.Member)
-                {
-                    fields.Add(ReadEval(node));
-                }
-                else
-                {
-                    throw new NotSupportedException("[QueryTranslator] groupByFields is not valid: must be New or Member");
-                }
-                sql += "\r\n group by " + String.Join(", ", fields);
-                #endregion
-
-                #region ##2 having
-                if (stream.having != null)
-                {
-                    var where = ReadEval(stream.having);
-                    if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n having " + where;
-                }
-                #endregion
-
-            }
-
-            // #5 OrderBy
-            if (stream.orders?.Any() == true)
-            {
-                var fields = stream.orders.Select(field => (sqlTranslator.GetSqlField(field.member) + " " + (field.asc ? "asc" : "desc"))).ToList();
-                sql += "\r\n order by " + String.Join(", ", fields);
-            }
-
-            // #6 limit 1000,10       limit {skip},{take}   |     limit {take}
-            if (stream.take != null || stream.skip != null)
-            {
-                if (stream.skip == null)
-                {
-                    sql += "\r\n limit " + stream.take;
-                }
-                else
-                {
-                    sql += "\r\n limit " + stream.skip + "," + (stream.take ?? 100000000);
-                }
-            }
-
-            return sql;
-        }
-
-        protected string ReadInnerTable(IStream stream)
-        {
-            if (stream is SourceStream sourceStream)
-            {
-                IQueryable query = sourceStream.GetSource() as IQueryable;
-                var tableName = sqlTranslator.GetTableName(query.ElementType);
-                return $"{sqlTranslator.DelimitIdentifier(tableName)} as " + stream.alias;
-            }
-            if (stream is CombinedStream baseStream)
-            {
-                var innerQuery = BuildQuery(baseStream);
-                return $"({innerQuery}) as " + stream.alias;
-            }
-            throw new NotSupportedException();
-        }
-
-
-
-        /// <summary>
-        /// read where or value or on
-        /// </summary>
-        /// <param name="data"></param>
-        /// <returns></returns>
-        /// <exception cref="NotSupportedException"></exception>
-        protected string ReadEval(ExpressionNode data)
-        {
-            switch (data.nodeType)
-            {
-
-                case NodeType.And:
-                    ExpressionNode_And and = data;
-                    return $"({ReadEval(and.left)}) and ({ReadEval(and.right)})";
-
-                case NodeType.Or:
-                    ExpressionNode_Or or = data;
-                    return $"({ReadEval(or.left)}) or ({ReadEval(or.right)})";
-
-                case NodeType.Not:
-                    ExpressionNode_Not not = data;
-                    return $"not ({ReadEval(not.body)})";
-
-                case NodeType.ArrayIndex:
-                    throw new NotSupportedException(data.nodeType);
-                //ExpressionNode_ArrayIndex arrayIndex = data;
-                //return Expression.ArrayIndex(ToExpression(arg, arrayIndex.left), ToExpression(arg, arrayIndex.right));
-                case NodeType.Equal:
-                case NodeType.NotEqual:
-                    {
-                        ExpressionNode_Binary binary = data;
-
-                        //   "= null"  ->   "is null" ,    "!=null" -> "is not null"   
-                        if (binary.right.nodeType == NodeType.Constant && binary.right.value == null)
-                        {
-                            var opera = data.nodeType == NodeType.Equal ? "is null" : "is not null";
-                            return $"{ReadEval(binary.left)} " + opera;
-                        }
-                        else if (binary.left.nodeType == NodeType.Constant && binary.left.value == null)
-                        {
-                            var opera = data.nodeType == NodeType.Equal ? "is null" : "is not null";
-                            return $"{ReadEval(binary.right)} " + opera;
-                        }
-
-                        var @operator = operatorMap[data.nodeType];
-                        return $"{ReadEval(binary.left)} {@operator} {ReadEval(binary.right)}";
-                    }
-                case NodeType.LessThan:
-                case NodeType.LessThanOrEqual:
-                case NodeType.GreaterThan:
-                case NodeType.GreaterThanOrEqual:
-                    {
-                        ExpressionNode_Binary binary = data;
-                        var @operator = operatorMap[data.nodeType];
-                        return $"{ReadEval(binary.left)} {@operator} {ReadEval(binary.right)}";
-                    }
-                case NodeType.MethodCall:
-                    {
-                        ExpressionNode_MethodCall call = data;
-
-                        switch (call.methodName)
-                        {
-                            case nameof(object.ToString):
-                                {
-                                    return $"cast({ReadEval(call.@object)} as text)";
-                                }
-
-                            #region String method:  StartsWith EndsWith Contains
-                            case nameof(string.StartsWith): // String.StartsWith
-                                {
-                                    var str = call.@object;
-                                    var value = call.arguments[0];
-                                    return $"{ReadEval(str)} like {ReadEval(value)}||'%'";
-                                }
-                            case nameof(string.EndsWith): // String.EndsWith
-                                {
-                                    var str = call.@object;
-                                    var value = call.arguments[0];
-                                    return $"{ReadEval(str)} like '%'||{ReadEval(value)}";
-                                }
-                            case nameof(string.Contains) when call.methodCall_typeName == "String": // String.Contains
-                                {
-                                    var str = call.@object;
-                                    var value = call.arguments[0];
-                                    return $"{ReadEval(str)} like '%'||{ReadEval(value)}||'%'";
-                                }
-                            #endregion
-
-                            case nameof(Enumerable.Contains):
-                                {
-                                    var values = call.arguments[0];
-                                    var member = call.arguments[1];
-                                    return $"{ReadEval(member)} in {ReadEval(values)}";
-                                }
-
-                            case nameof(DbFunction.Call):
-                                {
-                                    var functionName = call.arguments[0].value as string;
-                                    var argList = call.arguments.AsQueryable().Skip(1).Select(arg => ReadEval(arg)).ToList();
-                                    var arg = string.Join(",", argList);
-                                    return $"{functionName}({arg})";
-                                }
-                        }
-                        throw new NotSupportedException("[QueryTranslator] not suported MethodCall: " + call.methodName);
-                    }
-
-
-                #region Read Value
-
-                case NodeType.Member:
-                    return sqlTranslator.GetSqlField(data);
-
-                case NodeType.Constant:
-                    ExpressionNode_Constant constant = data;
-                    var paramName = NewParamName();
-                    sqlParam[paramName] = constant.value;
-                    return "@" + paramName;
-
-                case NodeType.Convert:
-                    {
-                        // cast( 4.1 as signed)
-
-                        ExpressionNode_Convert convert = data;
-
-                        Type targetType = convert.valueType?.ToType();
-
-                        if (targetType == typeof(object)) return ReadEval(convert.body);
-
-                        // Nullable
-                        if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
-
-                        string targetDbType = GetDbType(targetType);
-
-                        var sourceType = convert.body.Member_GetType();
-                        if (sourceType != null)
-                        {
-                            if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
-
-                            if (targetDbType == GetDbType(sourceType)) return ReadEval(convert.body);
-                        }
-
-                        if (targetDbType == "datetime")
-                        {
-                            return $"DATETIME({ReadEval(convert.body)})";
-                        }
-                        return $"cast({ReadEval(convert.body)} as {targetDbType})";
-
-                        #region GetDbType
-                        string GetDbType(Type type)
-                        {
-                            if (type == typeof(DateTime))
-                                return "datetime";
-
-                            if (type == typeof(string))
-                                return "text";
-
-                            if (type == typeof(float) || type == typeof(double) || type == typeof(decimal))
-                                return "numeric";
-
-                            if (type == typeof(bool) || type.Name.ToLower().Contains("int")) return "integer";
-
-                            throw new NotSupportedException("[QueryTranslator] unsupported column type:" + type.Name);
-                        }
-                        #endregion
-                    }
-                case nameof(ExpressionType.Add):
-                    {
-                        ExpressionNode_Binary binary = data;
-
-                        // ##1 String Add
-                        if (data.valueType?.ToType() == typeof(string))
-                        {
-                            return $"{ReadEval(binary.left)} || {ReadEval(binary.right)}";
-                        }
-
-                        // ##2 Numberic Add
-                        return $"{ReadEval(binary.left)} + {ReadEval(binary.right)}";
-                    }
-                case nameof(ExpressionType.Coalesce):
-                    {
-                        ExpressionNode_Binary binary = data;
-                        return $"COALESCE({ReadEval(binary.left)},{ReadEval(binary.right)})";
-                    }
-                    #endregion
-            }
-            throw new NotSupportedException("[QueryTranslator] not suported nodeType: " + data.nodeType);
-        }
-
-
-
-
-
-        protected readonly static Dictionary<string, string> operatorMap = new Dictionary<string, string>
-        {
-            [NodeType.Equal] = "=",
-            [NodeType.NotEqual] = "!=",
-            [NodeType.LessThan] = "<",
-            [NodeType.LessThanOrEqual] = "<=",
-            [NodeType.GreaterThan] = ">",
-            [NodeType.GreaterThanOrEqual] = ">=",
-        };
-
-    }
-
-}

+ 0 - 130
src/Vit.Orm/Sql/Translator/QueryTranslator.cs

@@ -1,130 +0,0 @@
-using System;
-using System.Linq;
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Orm.DataReader;
-using Vit.Orm.Sql.DataReader;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Extensions.Linq_Extensions;
-
-
-namespace Vit.Orm.Sql.Translator
-{
-    public class QueryTranslator : BaseQueryTranslator
-    {
-        /* //sql
-        select u.id, u.name, u.birth ,u.fatherId ,u.motherId,    father.name,  mother.name
-        from User u
-        inner join User father on u.fatherId = father.id 
-        left join User mother on u.motherId = mother.id
-        where u.id > 1
-        limit 1,5;
-         */
-
-
-        /// <summary>
-        /// only used for Method ReadSelect
-        /// </summary>
-        public Type entityType { get; private set; }
-
-        public QueryTranslator(SqlTranslator sqlTranslator, Type entityType) : base(sqlTranslator)
-        {
-            this.entityType = entityType;
-        }
-
-
-        protected override string ReadSelect(CombinedStream stream)
-        {
-            switch (stream.method)
-            {
-                case "Count":
-                    {
-                        var reader = new NumScalarReader();
-                        if (this.dataReader == null) this.dataReader = reader;
-                        return "count(*)";
-                    }
-                case "" or null or "ToList" or nameof(Queryable_Extensions.ToExecuteString):
-                    {
-                        var reader = new EntityReader();
-                        return BuildReader(reader);
-                    }
-                case "FirstOrDefault" or "First" or "LastOrDefault" or "Last":
-                    {
-                        stream.take = 1;
-                        stream.skip = null;
-
-                        if (stream.method.Contains("Last"))
-                            ReverseOrder(stream);
-
-                        var nullable = stream.method.Contains("OrDefault");
-                        var reader = new FirstEntityReader { nullable = nullable };
-                        return BuildReader(reader);
-                    }
-            }
-            throw new NotSupportedException("not supported method: " + stream.method);
-
-
-            #region BuildReader
-            string BuildReader(EntityReader reader)
-            {
-                var resultEntityType = entityType;
-                ExpressionNode selectedFields = stream.select?.fields as ExpressionNode;
-                if (selectedFields == null)
-                {
-                    if (stream.joins?.Any() != true)
-                    {
-                        selectedFields = ExpressionNode.Member(parameterName: stream.source.alias, memberName: null).Member_SetType(resultEntityType);
-                    }
-                }
-
-                if (selectedFields == null)
-                    throw new NotSupportedException("select could not be null");
-
-                if (resultEntityType == null && selectedFields.nodeType == NodeType.New)
-                {
-                    resultEntityType = selectedFields.New_GetType();
-                }
-
-                //if (resultEntityType == null)
-                //    throw new NotSupportedException("resultEntityType could not be null");
-
-                var sqlFields = reader.BuildSelect(resultEntityType, sqlTranslator, sqlTranslator.dbContext.convertService, selectedFields);
-                if (dataReader == null) dataReader = reader;
-                return (stream.distinct == true ? "distinct " : "") + sqlFields;
-            }
-            #endregion
-        }
-        void ReverseOrder(CombinedStream stream)
-        {
-            stream.orders ??= new();
-            var orders = stream.orders;
-            // make sure orders exist
-            if (!orders.Any())
-            {
-                AddOrder(stream.source);
-                stream.joins?.ForEach(right => AddOrder(right.right));
-
-                #region AddOrder
-                void AddOrder(IStream source)
-                {
-                    if (source is SourceStream sourceStream)
-                    {
-                        var entityType = sourceStream.GetEntityType();
-                        var entityDescriptor = sqlTranslator.GetEntityDescriptor(entityType);
-                        if (entityDescriptor != null)
-                        {
-                            var member = ExpressionNode_RenameableMember.Member(stream: source, entityType);
-                            member.memberName = entityDescriptor.keyName;
-                            orders.Add(new OrderField { member = member, asc = true });
-                        }
-                    }
-                }
-                #endregion
-            }
-
-            // reverse order
-            orders?.ForEach(order => order.asc = !order.asc);
-        }
-
-
-    }
-}

+ 0 - 256
src/Vit.Orm/Sql/Translator/SqlTranslator.cs

@@ -1,256 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-using Vit.Linq.ExpressionTree.ComponentModel;
-using Vit.Linq.ExpressionTree.CollectionsQuery;
-using Vit.Orm.Entity;
-using System.Linq;
-
-namespace Vit.Orm.Sql.Translator
-{
-    public abstract class SqlTranslator : ISqlTranslator
-    {
-
-        public DbContext dbContext { get; private set; }
-
-        public SqlTranslator(DbContext dbContext)
-        {
-            this.dbContext = dbContext;
-        }
-
-
-        public virtual IEntityDescriptor GetEntityDescriptor(Type entityType) => dbContext.GetEntityDescriptor(entityType);
-
-
-
-
-
-        #region DelimitIdentifier
-        /// <summary>
-        ///     Generates the delimited SQL representation of an identifier (column name, table name, etc.).
-        /// </summary>
-        /// <param name="identifier">The identifier to delimit.</param>
-        /// <returns>
-        ///     The generated string.
-        /// </returns>
-        public virtual string DelimitIdentifier(string identifier) => $"\"{EscapeIdentifier(identifier)}\""; // Interpolation okay; strings
-
-        /// <summary>
-        ///     Generates the escaped SQL representation of an identifier (column name, table name, etc.).
-        /// </summary>
-        /// <param name="identifier">The identifier to be escaped.</param>
-        /// <returns>
-        ///     The generated string.
-        /// </returns>
-        public virtual string EscapeIdentifier(string identifier) => identifier.Replace("\"", "\"\"");
-
-        /// <summary>
-        ///     Generates a valid parameter name for the given candidate name.
-        /// </summary>
-        /// <param name="name">The candidate name for the parameter.</param>
-        /// <returns>
-        ///     A valid name based on the candidate name.
-        /// </returns>
-        public virtual string GenerateParameterName(string name) => name.StartsWith("@", StringComparison.Ordinal) ? name : "@" + name;
-        #endregion
-
-
-
-
-
-        public virtual string GetTableName(Type entityType)
-        {
-            return dbContext.GetEntityDescriptor(entityType)?.tableName;
-        }
-
-        public virtual string GetSqlField(string tableName, string columnName)
-        {
-            return $"{DelimitIdentifier(tableName)}.{DelimitIdentifier(columnName)}";
-        }
-
-
-        public virtual string GetSqlField(ExpressionNode_Member member)
-        {
-            var memberName = member.memberName;
-            if (string.IsNullOrWhiteSpace(memberName))
-            {
-                memberName = dbContext.GetEntityDescriptor(member.Member_GetType())?.keyName;
-            }
-
-            // 1: {"nodeType":"Member","parameterName":"a0","memberName":"id"}
-            // 2: {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
-            return GetSqlField(member.objectValue?.parameterName ?? member.parameterName, memberName);
-        }
-
-
-        /// <summary>
-        /// functionName example:  Count, Max, Min, Sum, Average
-        /// </summary>
-        /// <param name="functionName"></param>
-        /// <param name="tableName"></param>
-        /// <param name="columnName"></param>
-        /// <returns></returns>
-        public virtual string GetSqlField_Aggregate(string functionName, string tableName, string columnName)
-        {
-            switch (functionName)
-            {
-                case nameof(Enumerable.Count):
-                    {
-                        if (columnName == null) return $"{functionName}(*)";
-                        return $"{functionName}({GetSqlField(tableName, columnName)})";
-                    }
-                case nameof(Enumerable.Max) or nameof(Enumerable.Min) or nameof(Enumerable.Sum):
-                    {
-                        return $"{functionName}({GetSqlField(tableName, columnName)})";
-                    }
-                case nameof(Enumerable.Average):
-                    {
-                        return $"AVG({GetSqlField(tableName, columnName)})";
-                    }
-            }
-            throw new NotSupportedException("[SqlTranslator] unsupported aggregate function : " + functionName);
-        }
-
-
-
-        public abstract string PrepareCreate(IEntityDescriptor entityDescriptor);
-        protected abstract string GetDbType(Type type);
-
-
-        public virtual string PrepareGet<Entity>(DbSet<Entity> dbSet)
-        {
-            /* //sql
-            delete from user where id = 7;
-            */
-            var entityDescriptor = dbSet.entityDescriptor;
-
-            // #2 build sql
-            string sql = $@"select * from {DelimitIdentifier(entityDescriptor.tableName)} where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)};";
-
-            return sql;
-        }
-
-        public virtual (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(CombinedStream combinedStream, Type entityType)
-        {
-            var query = new QueryTranslator(this, entityType: entityType);
-            string sql = query.BuildQuery(combinedStream);
-            return (sql, query.sqlParam, query.dataReader);
-        }
-
-        public abstract (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(CombinedStream combinedStream);
-
-        public abstract (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(CombinedStream combinedStream);
-
-        public virtual (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) PrepareAdd<Entity>(DbSet<Entity> dbSet)
-        {
-            /* //sql
-             insert into user(name,birth,fatherId,motherId) values('','','');
-             select seq from sqlite_sequence where name='user';
-              */
-            var entityDescriptor = dbSet.entityDescriptor;
-
-            var columns = entityDescriptor.columns;
-
-            // #1 GetSqlParams 
-            Func<Entity, Dictionary<string, object>> GetSqlParams = (entity) =>
-                {
-                    var sqlParam = new Dictionary<string, object>();
-                    foreach (var column in columns)
-                    {
-                        var columnName = column.name;
-                        var value = column.Get(entity);
-
-                        sqlParam[columnName] = value;
-                    }
-                    return sqlParam;
-                };
-
-            #region #2 columns 
-            List<string> columnNames = new List<string>();
-            List<string> valueParams = new List<string>();
-            string columnName;
-
-            foreach (var column in columns)
-            {
-                columnName = column.name;
-
-                columnNames.Add(DelimitIdentifier(columnName));
-                valueParams.Add(GenerateParameterName(columnName));
-            }
-            #endregion
-
-            // #3 build sql
-            string sql = $@"insert into {DelimitIdentifier(entityDescriptor.tableName)}({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
-            //sql+=$"select seq from sqlite_sequence where name = '{tableName}'; ";
-
-            return (sql, GetSqlParams);
-        }
-
-        public virtual (string sql, Func<Entity, Dictionary<string, object>> GetSqlParams) PrepareUpdate<Entity>(DbSet<Entity> dbSet)
-        {
-            /* //sql
-                update user set name='' where id=7;
-            */
-
-            var entityDescriptor = dbSet.entityDescriptor;
-            var sqlParam = new Dictionary<string, object>();
-
-            // #1 GetSqlParams
-            Func<Entity, Dictionary<string, object>> GetSqlParams = (entity) =>
-            {
-                var sqlParam = new Dictionary<string, object>();
-                foreach (var column in entityDescriptor.allColumns)
-                {
-                    var columnName = column.name;
-                    var value = column.Get(entity);
-
-                    sqlParam[columnName] = value;
-                }
-                //sqlParam[entityDescriptor.keyName] = entityDescriptor.key.Get(entity);
-                return sqlParam;
-            };
-
-            // #2 columns
-            List<string> columnsToUpdate = new List<string>();
-            string columnName;
-            foreach (var column in entityDescriptor.columns)
-            {
-                columnName = column.name;
-                columnsToUpdate.Add($"{DelimitIdentifier(columnName)}={GenerateParameterName(columnName)}");
-            }
-
-            // #3 build sql
-            string sql = $@"update {DelimitIdentifier(entityDescriptor.tableName)} set {string.Join(",", columnsToUpdate)} where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)};";
-
-            return (sql, GetSqlParams);
-        }
-
-
-        public virtual string PrepareDelete<Entity>(DbSet<Entity> dbSet)
-        {
-            /* //sql
-            delete from user where id = 7;
-            */
-            var entityDescriptor = dbSet.entityDescriptor;
-
-            // #2 build sql
-            string sql = $@"delete from {DelimitIdentifier(entityDescriptor.tableName)} where {DelimitIdentifier(entityDescriptor.keyName)}={GenerateParameterName(entityDescriptor.keyName)};";
-
-            return sql;
-        }
-
-        public virtual string PrepareDeleteRange<Entity>(DbSet<Entity> dbSet)
-        {
-            /* //sql
-            delete from user where id in ( 7 ) ;
-            */
-            var entityDescriptor = dbSet.entityDescriptor;
-
-            // #2 build sql
-            string sql = $@"delete from {DelimitIdentifier(entityDescriptor.tableName)} where {DelimitIdentifier(entityDescriptor.keyName)} in {GenerateParameterName("keys")};";
-
-            return sql;
-        }
-
-    }
-}