Lith 11 месяцев назад
Родитель
Сommit
4751d8a2bf
100 измененных файлов с 6548 добавлено и 640 удалено
  1. 26 0
      Vit.Linq.sln
  2. 94 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/DataSource.cs
  3. 104 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Any_Test.cs
  4. 68 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/ExpressionConvertor/New_Test.cs
  5. 79 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/ExpressionNodeConvert_Test.cs
  6. 89 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/GroupBy_Test.cs
  7. 247 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Join_Test.cs
  8. 147 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Queryable_Test.cs
  9. 75 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Serialize_Test.cs
  10. 359 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/ValueType_Test.cs
  11. 50 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/README.md
  12. 28 0
      src/Test/Vit.Linq.ExpressionTree.MsTest/Vit.Linq.ExpressionTree.MsTest.csproj
  13. 0 1
      src/Test/Vit.Linq.MsTest/Vit.Linq.MsTest.csproj
  14. 55 0
      src/Test/Vit.Orm.Sqlite.MsTest/Query_Test.cs
  15. 287 0
      src/Test/Vit.Orm.Sqlite.MsTest/Queryable_Test.cs
  16. 52 0
      src/Test/Vit.Orm.Sqlite.MsTest/TestData.cs
  17. 22 0
      src/Test/Vit.Orm.Sqlite.MsTest/Vit.Orm.Sqlite.MsTest.csproj
  18. 46 0
      src/Vit.Linq/ExpressionTree/CodeConvertArgument.cs
  19. 183 0
      src/Vit.Linq/ExpressionTree/ComponentModel/CollectionQuery/QueryAction.LoadFromNode.cs
  20. 36 0
      src/Vit.Linq/ExpressionTree/ComponentModel/CollectionQuery/QueryAction.cs
  21. 470 0
      src/Vit.Linq/ExpressionTree/ComponentModel/CollectionsQuery/StreamReader.cs
  22. 156 0
      src/Vit.Linq/ExpressionTree/ComponentModel/CollectionsQuery/Streams.cs
  23. 0 43
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode.Binary.cs
  24. 0 15
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode.cs
  25. 13 8
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode.AndOr.cs
  26. 41 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode.Binary.cs
  27. 37 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode.cs
  28. 58 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNodeCloner.cs
  29. 8 3
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_ArrayIndex.cs
  30. 11 19
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Constant.cs
  31. 17 8
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Convert.cs
  32. 48 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Lambda.cs
  33. 116 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Member.cs
  34. 50 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_MethodCall.cs
  35. 51 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_New.cs
  36. 29 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Not.cs
  37. 18 0
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/SortField.cs
  38. 0 47
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Call.cs
  39. 0 21
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Lambda.cs
  40. 0 21
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Member.cs
  41. 0 17
      src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Not.cs
  42. 18 17
      src/Vit.Linq/ExpressionTree/ComponentModel/NodeType.cs
  43. 144 14
      src/Vit.Linq/ExpressionTree/ComponentModel/ValueType.cs
  44. 97 0
      src/Vit.Linq/ExpressionTree/DataConvertArgument.cs
  45. 0 32
      src/Vit.Linq/ExpressionTree/ExpressionConvertService.MethodCall.cs
  46. 0 18
      src/Vit.Linq/ExpressionTree/ExpressionConvertService.Serialize.cs
  47. 42 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToCode.Extensions.cs
  48. 11 135
      src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToCode.cs
  49. 15 212
      src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToData.cs
  50. 36 1
      src/Vit.Linq/ExpressionTree/ExpressionConvertService.cs
  51. 108 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Binary.cs
  52. 38 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Binarys/Coalesce.cs
  53. 47 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Conditional.cs
  54. 83 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Constant.cs
  55. 56 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Lambda.cs
  56. 70 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Member.cs
  57. 107 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCall.cs
  58. 15 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/IMethodConvertor.cs
  59. 36 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/InstanceMethod.cs
  60. 63 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/MethodConvertor_Base.cs
  61. 32 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/MethodConvertor_Common.cs
  62. 38 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/MethodConvertor_Delegate.cs
  63. 74 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Others.cs
  64. 49 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Extensions_Methods/Queryable_Methods.cs
  65. 30 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Extensions_Methods/TotalCount.cs
  66. 58 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Any.cs
  67. 64 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Join.cs
  68. 73 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/OrderBy.cs
  69. 48 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Queryable_Methods.cs
  70. 60 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Select.cs
  71. 55 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/SelectMany.cs
  72. 31 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Skip.cs
  73. 31 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Take.cs
  74. 55 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Where.cs
  75. 73 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/String_Methods.cs
  76. 89 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/ModelGenerator.cs
  77. 133 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/New.cs
  78. 80 0
      src/Vit.Linq/ExpressionTree/ExpressionConvertor/Unary.cs
  79. 22 0
      src/Vit.Linq/ExpressionTree/Extensions/Extensions/Queryable_Extensions_ExecuteDelete.cs
  80. 23 0
      src/Vit.Linq/ExpressionTree/Extensions/Extensions/Queryable_Extensions_ExecuteUpdate.cs
  81. 22 0
      src/Vit.Linq/ExpressionTree/Extensions/Extensions/Queryable_Extensions_ToSql.cs
  82. 27 0
      src/Vit.Linq/ExpressionTree/Extensions/Queryable_Extensions_TotalCount.cs
  83. 102 0
      src/Vit.Linq/ExpressionTree/Extensions/Queryable_OrderBy_Extensions.cs
  84. 15 0
      src/Vit.Linq/ExpressionTree/IExpressionConvertor.cs
  85. 6 6
      src/Vit.Linq/QueryableBuilder.cs
  86. 6 2
      src/Vit.Linq/Vit.Linq.csproj
  87. 40 0
      src/Vit.Orm.Sqlite/Extensions/DbContext_Extensions.cs
  88. 87 0
      src/Vit.Orm.Sqlite/Sql/BatchUpdateTranslator.cs
  89. 315 0
      src/Vit.Orm.Sqlite/Sql/QueryTranslator.cs
  90. 220 0
      src/Vit.Orm.Sqlite/Sql/SqlTranslator.cs
  91. 17 0
      src/Vit.Orm.Sqlite/Vit.Orm.Sqlite.csproj
  92. 40 0
      src/Vit.Orm/DbContext.cs
  93. 25 0
      src/Vit.Orm/DbSet.cs
  94. 31 0
      src/Vit.Orm/Entity/ColumnDescriptor.cs
  95. 60 0
      src/Vit.Orm/Entity/Dapper/EntityDescriptor.cs
  96. 13 0
      src/Vit.Orm/Entity/IColumnDescriptor.cs
  97. 22 0
      src/Vit.Orm/Entity/IEntityDescriptor.cs
  98. 104 0
      src/Vit.Orm/README.md
  99. 108 0
      src/Vit.Orm/Sql/DataReader/EntityReader.cs
  100. 14 0
      src/Vit.Orm/Sql/DataReader/EntityReader/IArgReader.cs

+ 26 - 0
Vit.Linq.sln

@@ -16,6 +16,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{75C25D0B-852
 		README.md = README.md
 	EndProjectSection
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Linq.ExpressionTree.MsTest", "src\Test\Vit.Linq.ExpressionTree.MsTest\Vit.Linq.ExpressionTree.MsTest.csproj", "{5A6CE1E2-07A4-4C8D-AB99-D7AED536DAA6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Orm", "src\Vit.Orm\Vit.Orm.csproj", "{952A36C2-CE2C-416F-BFD0-EBDCA8A3056E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Orm.Sqlite", "src\Vit.Orm.Sqlite\Vit.Orm.Sqlite.csproj", "{B8F1010C-9DC7-4C5A-B0B5-778279F2EDC9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Orm.Sqlite.MsTest", "src\Test\Vit.Orm.Sqlite.MsTest\Vit.Orm.Sqlite.MsTest.csproj", "{C8F89EA5-5DB5-40C4-BF4B-6E31A3C0CB73}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -34,6 +42,22 @@ Global
 		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5A6CE1E2-07A4-4C8D-AB99-D7AED536DAA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5A6CE1E2-07A4-4C8D-AB99-D7AED536DAA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5A6CE1E2-07A4-4C8D-AB99-D7AED536DAA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5A6CE1E2-07A4-4C8D-AB99-D7AED536DAA6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{952A36C2-CE2C-416F-BFD0-EBDCA8A3056E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{952A36C2-CE2C-416F-BFD0-EBDCA8A3056E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{952A36C2-CE2C-416F-BFD0-EBDCA8A3056E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{952A36C2-CE2C-416F-BFD0-EBDCA8A3056E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B8F1010C-9DC7-4C5A-B0B5-778279F2EDC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B8F1010C-9DC7-4C5A-B0B5-778279F2EDC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B8F1010C-9DC7-4C5A-B0B5-778279F2EDC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B8F1010C-9DC7-4C5A-B0B5-778279F2EDC9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C8F89EA5-5DB5-40C4-BF4B-6E31A3C0CB73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C8F89EA5-5DB5-40C4-BF4B-6E31A3C0CB73}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C8F89EA5-5DB5-40C4-BF4B-6E31A3C0CB73}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C8F89EA5-5DB5-40C4-BF4B-6E31A3C0CB73}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -41,6 +65,8 @@ Global
 	GlobalSection(NestedProjects) = preSolution
 		{5801E323-C03D-48D2-BEFF-DE060B3293B0} = {0062F400-558C-4084-8004-3C8D4CBBFDE4}
 		{7E513E2C-BF52-4662-AD8F-5910F283178D} = {0062F400-558C-4084-8004-3C8D4CBBFDE4}
+		{5A6CE1E2-07A4-4C8D-AB99-D7AED536DAA6} = {0062F400-558C-4084-8004-3C8D4CBBFDE4}
+		{C8F89EA5-5DB5-40C4-BF4B-6E31A3C0CB73} = {0062F400-558C-4084-8004-3C8D4CBBFDE4}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {C7DA16E3-9949-49FA-B0B4-F830636DE60F}

+ 94 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/DataSource.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Vit.Linq.MsTest
+{
+    public class Person
+    {
+        public int id;
+        public int? departmentId;
+        public string name { get; set; }
+        public DateTime addTime;
+        public string ext;
+        public bool isEven;
+
+        public Job job;
+
+        public Job[] jobArray;
+
+        public List<Job> jobList;
+
+        public Person PopulateJob()
+        {
+            job = new Job { departmentId = departmentId, personId = id, name = name + "_job1" };
+            var job2 = new Job { departmentId = departmentId, personId = id, name = name + "_job2" };
+
+
+            if (id % 2 == 0)
+                jobArray = new[] { job, job2 };
+            else
+                jobArray = new[] { job };
+
+            jobList = jobArray.ToList();
+
+            return this;
+        }
+
+        public int GetJobCount()
+        {
+            return jobList.Count;
+        }
+        public bool JobExistAtIndex(int index)
+        {
+            if (index < jobList.Count) return true;
+            return false;
+        }
+        public Job GetJobAtIndex(int index)
+        {
+            if (index < jobList.Count) return jobList[index];
+            return null;
+        }
+    }
+
+    public class Job
+    {
+        public int? departmentId;
+ 
+        public int? personId;
+        public string name;
+        public string GetJobName()
+        {
+            return name;
+        }
+    }
+
+
+
+    public class DataSource
+    {
+        public static List<Person> BuildDataSource(int count = 1000)
+        {
+            var Now = DateTime.Now;
+            var list = new List<Person>(count);
+            for (int i = 0; i < count; i++)
+            {
+                list.Add(new Person
+                {
+                    id = i,
+                    departmentId = i / 10,
+                    name = "name" + i,
+                    addTime = Now.AddSeconds(i),
+                    ext = "ext" + i,
+                    isEven = i % 2 == 0
+                }.PopulateJob());
+
+            }
+            return list;
+        }
+
+        public static IQueryable GetIQueryable() => GetQueryable();
+        public static IQueryable<Person> GetQueryable() => BuildDataSource().AsQueryable();
+    }
+}

+ 104 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Any_Test.cs

@@ -0,0 +1,104 @@
+using System.Linq.Expressions;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+using System.Linq;
+using System;
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel.CollectionQuery;
+
+namespace Vit.Linq.MsTest.Converter
+{
+    [TestClass]
+    public class Any_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+            {
+                Func<Expression, Type, object> QueryExecutor = (expression,type) =>
+                {
+                    var convertService = ExpressionConvertService.Instance;
+                    var node = convertService.ConvertToData(expression);
+
+                    var strNode = Json.Serialize(node);
+                    // ToLambdaExpression       
+                    var exp3 = (Expression<Func<IQueryable<Person>, IQueryable<Person>>>)convertService.ToLambdaExpression(node, typeof(IQueryable<Person>));
+                    var predicate_ = exp3.Compile();
+
+                    // As Filter
+                    var queryAction = new QueryAction(node);
+                    var strQuery = Json.Serialize(queryAction);
+                    var predicate = convertService.ToPredicateExpression<Person>(queryAction.filter, "person");
+                    //var lambdaExp = convertService.ToLambdaExpression(queryAction.filter,typeof(Person), "person");
+
+
+                    //var query2 = new ExpressionConverter().ConvertToQueryAction(exp2);
+                    //var strQuery2 = Json.Serialize(query2);
+
+                    var list = DataSource.GetQueryable().Where(predicate);
+
+
+                    list = list.OrderBy(queryAction.orders);
+
+                    var methodName = queryAction.method;
+
+                    if (methodName == "TotalCount") return list.Count();
+
+                    if (queryAction.skip.HasValue)
+                        list = list.Skip(queryAction.skip.Value);
+                    if (queryAction.take.HasValue)
+                        list = list.Take(queryAction.take.Value);
+
+                    switch (methodName)
+                    {
+                        case "First": return list.First();
+                        case "FirstOrDefault": return list.FirstOrDefault();
+                        case "Last": return list.Last();
+                        case "LastOrDefault": return list.LastOrDefault();
+                        case "Count": return list.Count();
+                    }
+
+                    // ToList
+                    return list;
+                };
+
+                var query = QueryableBuilder.Build<Person>(QueryExecutor);
+
+                var persons = new List<Person> { new Person { id = 2 }, new Person { id = 3 } }.AsQueryable();
+                var pids = persons.Select(m => m.id).ToList();
+
+
+                var jobs = new List<Job> { new Job { departmentId = 1 }, new Job { departmentId = 2 } };
+                //var joins = query.Join(jobs, outer => outer.departmentId, inner => inner.departmentId, (left, right) => new { person = left, job = right });
+                //var result = joins.ToList();
+
+                //var joins = query.Join(jobs, outer => outer.departmentId, inner => inner.departmentId, (left, right) => new Person{departmentId = right.departmentId,name=left.name });
+                //var result = joins.ToList();
+
+
+                query = query
+                        .Where(m => persons.Any(p => p.id == m.id)) // MethodCall Any
+                        ;
+
+                var list = query.ToList();
+
+                var count = query.Count();
+                var totalCount = query.TotalCount();
+            
+
+                Assert.AreEqual(5, list.Count);
+                Assert.AreEqual(17, list[0].id);
+                Assert.AreEqual(15, list[1].id);
+            }
+
+
+        }
+
+
+
+
+    }
+}

+ 68 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/ExpressionConvertor/New_Test.cs

@@ -0,0 +1,68 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Core.Module.Serialization;
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.MsTest.New.test2.ExpressionConvertor
+{
+    [TestClass]
+    public class New_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+
+
+            Func<Expression,  Type, object> QueryExecutor = (expression, type) =>
+            {
+                var convertService = ExpressionConvertService.Instance;
+
+                ExpressionNode node;
+
+
+                #region ExpressinNode
+                {
+                    // #1 Code to Data
+                    // (query) => query.Where().OrderBy().Skip().Take().Select().ToList();
+                    node = convertService.ConvertToData(expression);
+
+                    // {"nodeType":"Lambda","parameterNames":["Param_1"],"body":{"nodeType":"Call","methodName":"Where","arguments":[{"parameterName":"Param_1","nodeType":"Member"},{"nodeType":"Lambda","parameterNames":["Param_0"],"body":{"left":{"nodeType":"Member","parameterName":"Param_0","memberName":"id"},"right":{"nodeType":"Constant","valueType":{"typeName":"Int32"},"value":10},"nodeType":"GreaterThanOrEqual"}}]}}
+                    var strNode = Json.Serialize(node);
+
+                    // #2 Data to Code
+                    // Param_1 => Param_1.Where(Param_0 => (Param_0.id >= 10))
+                    var lambdaExp = convertService.ToLambdaExpression(node, typeof(IQueryable<Person>));
+                    //var exp3 = (Expression<Func<IQueryable<Person>, IQueryable<Person>>>)lambdaExp;
+                    var predicate = lambdaExp.Compile();
+                }
+                #endregion
+
+ 
+
+                //var list = DataSource.GetQueryable().Where(predicate);
+
+
+               
+
+                throw new NotSupportedException("Method not support"  );
+            };
+
+            var query = QueryableBuilder.Build<Person>(QueryExecutor);
+
+
+
+
+            var list = query.Select(p => new { name =  p.name }).ToList();
+            //Assert.AreEqual(5, list.Count);
+            //Assert.AreEqual(17, list[0].id);
+            //Assert.AreEqual(15, list[1].id);
+        }
+
+
+    }
+}

+ 79 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/ExpressionNodeConvert_Test.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Core.Module.Serialization;
+using Vit.Linq.ExpressionTree;
+
+namespace Vit.Linq.MsTest.Converter
+{
+    [TestClass]
+    public class ExpressionNodeConvert_Test
+    {
+
+
+        void Test(Expression<Func<Person, bool>> predicate)
+        {
+            var convert = ExpressionConvertService.Instance;
+            var node = convert.ConvertToData(predicate);
+            var str = Json.Serialize(node);
+            var convertedPredicate = convert.ToPredicate<Person>(node);
+
+
+            IQueryable<Person> queryable = DataSource.GetQueryable();
+
+            var result1 = queryable.Where(predicate).ToList();
+            var result2 = queryable.Where(convertedPredicate).ToList();
+
+
+            Assert.AreEqual(result1.Count, result2.Count);
+            for (var t = 0; t < result1.Count; t++)
+            {
+                Assert.AreEqual(result1[t].id, result2[t].id);
+            }
+        }
+
+
+        [TestMethod]
+        public void Test()
+        {
+            {
+                var person = new { isEven = true };
+                Expression<Func<Person, bool>> predicate = x => person.isEven && x.isEven;
+ 
+                Test(predicate);            
+            }
+
+            {
+                var person = new { names = new[] { "name2" }  };
+                Expression<Func<Person, bool>> predicate = x => x.name == person.names[0];
+
+                Test(predicate);
+            }
+
+            {
+                var person = new { names = new List<string> { "name2_job1" } };
+                Expression<Func<Person, bool>> predicate = x => x.jobArray[0].name == person.names[0];
+
+                Test(predicate);
+            }
+
+            {
+                var person = new { id = 10, name = "name5" };
+                Expression<Func<Person, bool>> predicate = x =>  x.id <= person.id && x.name == person.name;
+
+                Test(predicate);
+            }
+ 
+
+
+
+        }
+
+
+
+    }
+}

+ 89 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/GroupBy_Test.cs

@@ -0,0 +1,89 @@
+using System.Linq.Expressions;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.Converter;
+using Vit.Linq.Filter;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+using System.Linq;
+using System;
+
+namespace Vit.Linq.MsTest.Converter
+{
+    [TestClass]
+    public class GroupBy_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+            {
+                Func<Expression, Expression, Type, object> QueryExecutor = (expression,rootExpression,type) =>
+                {
+                    var convertService = ExpressionConvertService.Instance;
+                    var node = convertService.ConvertToData(expression, rootExpression);
+
+                    var strNode = Json.Serialize(node);
+                    // ToLambdaExpression       
+                    var exp3 = (Expression<Func<IQueryable<Person>, IQueryable<Person>>>)convertService.ToLambdaExpression(node, typeof(IQueryable<Person>));
+                    var predicate_ = exp3.Compile();
+
+                    // As Filter
+                    var queryAction = new QueryAction(node);
+                    var strQuery = Json.Serialize(queryAction);
+                    var predicate = convertService.ToPredicateExpression<Person>(queryAction.filter, "person");
+                    //var lambdaExp = convertService.ToLambdaExpression(queryAction.filter,typeof(Person), "person");
+
+
+                    //var query2 = new ExpressionConverter().ConvertToQueryAction(exp2);
+                    //var strQuery2 = Json.Serialize(query2);
+
+                    var list = DataSource.GetQueryable().Where(predicate);
+
+
+                    list = list.OrderBy(queryAction.orders);
+
+                    var methodName = queryAction.method;
+
+                    if (methodName == "TotalCount") return list.Count();
+
+                    if (queryAction.skip.HasValue)
+                        list = list.Skip(queryAction.skip.Value);
+                    if (queryAction.take.HasValue)
+                        list = list.Take(queryAction.take.Value);
+
+                    switch (methodName)
+                    {
+                        case "First": return list.First();
+                        case "FirstOrDefault": return list.FirstOrDefault();
+                        case "Last": return list.Last();
+                        case "LastOrDefault": return list.LastOrDefault();
+                        case "Count": return list.Count();
+                    }
+
+                    // ToList
+                    return list;
+                };
+
+                var query = QueryableBuilder.Build<Person>(QueryExecutor);
+
+                var groups = query.GroupBy(p => p.departmentId).ToList();
+
+
+                var list = groups.ToList();
+
+                var count = groups.Count();
+                var totalCount = groups.AsQueryable().TotalCount();
+
+
+                //Assert.AreEqual(5, list.Count); 
+            }
+
+
+        }
+
+
+
+
+    }
+}

+ 247 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Join_Test.cs

@@ -0,0 +1,247 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Core.Module.Serialization;
+using Vit.Extensions.Linq_Extensions;
+
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.MsTest.Converter
+{
+    [TestClass]
+    public class Join_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+            {
+                Func<Expression,  Type, object> QueryExecutor = (expression, type) =>
+                {
+                    var convertService = ExpressionConvertService.Instance;
+
+                    ExpressionNode node;
+                    Delegate predicate;
+
+                    #region get ExpressionNode
+                    {
+                        // (query,query2) => query.SelectMany(query2).Where().OrderBy().Skip().Take().Select().ToList();
+
+                        //  users
+                        // .SelectMany(
+                        //      user => users.Where(father => (Convert(father.id, Nullable`1) == user.fatherId)).DefaultIfEmpty(),
+                        //      (user, father) => new <>f__AnonymousType4`2(user = user, father = father)
+                        //  ).SelectMany(
+                        //      item => users.Where(mother => (Convert(mother.id, Nullable`1) == item.user.fatherId)).DefaultIfEmpty(),
+                        //      (item, mother) => new <>f__AnonymousType5`3(user = item.user, father = item.father, mother = mother)
+                        //  )
+                        node = convertService.ConvertToData(expression, autoReduce: true);
+
+                        var strNode = Json.Serialize(node); 
+                    }
+                    #endregion
+
+                    var stream = ExpressionTree.ComponentModel.CollectionsQuery.StreamReader.ReadNode(node);
+                    var strStream = Json.Serialize(stream);
+                    return null;
+                };
+
+
+                //LeftJoin/InnerJoin  (SelectMany) single and select
+                {
+                    var users = QueryableBuilder.Build<User>(QueryExecutor);
+
+                    var list = users
+                        .SelectMany(
+                            user => users.Where(father => father.id == user.fatherId).DefaultIfEmpty(),
+                            (user, father) => new { user1=user, father1= father, id1 = user.id }
+                        )
+                        .SelectMany(
+                            item => users.Where(mother => mother.id == item.user1.motherId).DefaultIfEmpty(),
+                            (item, mother) => new { user2 = item.user1, father2 = item.father1, mother2 = mother, id2 = item.id1, fatherId2 = item.father1.id }
+                        )
+                        //.SelectMany(
+                        //    item => users.Where(mother => mother.id == item.user.motherId).DefaultIfEmpty(),
+                        //    (item, mother) => new { item.user, father2 = item.father1, mother2 = mother, item.id, fatherId = item.father1.id }
+                        //)
+                        .Where(items => items.user2.id > 0)
+                        .Where(items => items.fatherId2 > 0)
+                        .Select((item) => new { 
+                            user9 = item.user2,     id9 = item.id2,
+                            father9 = item.father2, fatherId9 = item.fatherId2,
+                            mother9 = item.mother2, motherId9 = item.mother2.id 
+                         })
+                        .ToList();
+                }
+
+                //LeftJoin/InnerJoin  (SelectMany)
+                {
+                    var users = QueryableBuilder.Build<User>(QueryExecutor);
+
+                    var list = users
+                        .SelectMany(
+                            user => users.Where(father => father.id == user.fatherId).DefaultIfEmpty(),
+                            (user, father) => new { user, father }
+                        )
+                        .SelectMany(
+                            item => users.Where(mother => mother.id == item.user.fatherId).DefaultIfEmpty(),
+                            (item, mother) => new { item.user, item.father, mother }
+                        )
+                        .ToList();
+                }
+
+
+                //LeftJoin/InnerJoin  (SelectMany) single
+                {
+                    var users = QueryableBuilder.Build<User>(QueryExecutor);
+
+                    var list = users
+                        .SelectMany(
+                            user => users.Where(father => father.id == user.fatherId).DefaultIfEmpty(),
+                            (user, father) => new { user, father }
+                        )
+                        .Select((item) => new { item.user.id })
+                        .ToList();
+                }
+
+
+
+                {
+                    //var users = QueryableBuilder.Build<User>(QueryExecutor);
+
+                    //var list = users
+                    //    .SelectMany(
+                    //        user => users.Where(father => father.id == user.fatherId).DefaultIfEmpty(),
+                    //         // (user, father) => new { user, father } //NewExpression {new <>f__AnonymousType4`2(user = user, father = father)}
+                    //         (user, father) => new Result(user)   //NewExpression   new Result(user)
+                    //        // (user, father) => new Result { user = user, father = father } // MemberInitExpression new Result() {user = user, father = father}
+                    //     )
+                    //    .ToList();
+                }
+
+                #region #3 LeftJoin/InnerJoin  (SelectMany)
+
+
+                {
+                    var users = QueryableBuilder.Build<User>(QueryExecutor);
+/*
+{value(Vit.Linq.Converter.OrderedQueryable`1[Vit.Linq.MsTest.Converter.Join_Test+User])
+.SelectMany(
+    user => value(Vit.Linq.MsTest.Converter.Join_Test+<>c__DisplayClass0_0).users
+        .Where(f => (Convert(f.id, Nullable`1) == user.fatherId)).DefaultIfEmpty(),
+    (user, father) => new <>f__AnonymousType4`2(user = user, father = father)  )
+.SelectMany(
+    <>h__TransparentIdentifier0 => value(Vit.Linq.MsTest.Converter.Join_Test+<>c__DisplayClass0_0).users
+            .Where(m => ((Convert(m.id, Nullable`1) == <>h__TransparentIdentifier0.user.motherId) 
+                AndAlso (Convert(<>h__TransparentIdentifier0.father.id, Nullable`1) != m.fatherId))).DefaultIfEmpty(),
+    (<>h__TransparentIdentifier0, mother) => new <>f__AnonymousType5`3(
+        user = <>h__TransparentIdentifier0.user, 
+        father = <>h__TransparentIdentifier0.father,
+        mother = mother
+))
+*/
+                    var list = (from user in users
+                                from father in users.Where(f => f.id == user.fatherId).DefaultIfEmpty()
+                                from mother in users.Where(m => m.id == user.motherId && father.id != m.fatherId).DefaultIfEmpty()
+                                select new
+                                {
+                                    user = user,
+                                    father = father,
+                                    mother = mother
+                                }).ToList();
+
+                }
+
+
+
+                #region #1 Join
+                {
+                    //var lefts = QueryableBuilder.Build<Person>(QueryExecutor);
+                    //var rights = QueryableBuilder.Build<Person>(QueryExecutor);
+
+                    //var list = lefts.Join(
+                    //    rights,
+                    //    left => left.id,
+                    //    right => right.id,
+                    //    (left, right) => new Result
+                    //    {
+                    //        left = left,
+                    //        right = right
+                    //    }
+                    //).ToList();
+
+                    //Assert.AreEqual(1000, list.Count);
+                }
+                #endregion
+
+
+
+                #region #2 Join
+                {
+                    //var lefts = QueryableBuilder.Build<Person>(QueryExecutor);
+                    //var rights = QueryableBuilder.Build<Person>(QueryExecutor);
+
+                    //var list = (from left in lefts
+                    //            join right in rights on left.id equals right.id
+                    //            // into rights_
+                    //            // from right in rights_.DefaultIfEmpty()
+                    //            where left.id == right.id && right.isEven
+                    //            select new Result
+                    //            {
+                    //                left = left,
+                    //                right = right,
+                    //                id = left.id > 0 ? left.id : right.id,
+                    //                departmentId = (right ?? left).departmentId
+                    //            }
+                    //    ).ToList();
+
+                    //Assert.AreEqual(500, list.Count);
+                }
+                #endregion
+
+
+              
+                #endregion
+
+
+                //var users = new List<Person> { new Person { departmentId = 1, id = 1 }, new Person { departmentId = 2, id = 21 } };
+                //var departs = new[] { new { departmentId = 1, name = "part1" }, new { departmentId = 2, name = "part1" } };
+                //var userInfos2 = users.Join(departs, user => user.departmentId, depart => depart.departmentId, (user, depart) => new { user, depart });
+                //var userInfos = users.AsQueryable().Join(departs, user => user.departmentId, depart => depart.departmentId, (user, depart) => new { user, depart });
+
+
+            }
+
+
+
+
+        }
+        class Result
+        {
+            public Result() { }
+            public Result(User user) { this.user = user; }
+            public User user;
+            public User father { get; set; }
+            public int? id { get; set; }
+            public int? departmentId { get; set; }  
+        }
+
+
+        public class User
+        {
+            
+            //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+            public int id { get; set; }
+            public string name { get; set; }
+            public DateTime? birth { get; set; }
+
+            public int? fatherId { get; set; }
+            public int? motherId { get; set; }
+        }
+
+
+    }
+}

+ 147 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Queryable_Test.cs

@@ -0,0 +1,147 @@
+using System.Linq.Expressions;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using System;
+using Vit.Linq.ExpressionTree.ComponentModel.CollectionQuery;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace Vit.Linq.MsTest.Converter
+{
+    // rootExpression?
+    [TestClass]
+    public class Queryable_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+            {
+                Func<Expression,  Type, object> QueryExecutor = (expression,type) =>
+                {
+                    var convertService = ExpressionConvertService.Instance;
+
+                    ExpressionNode node;
+                    
+
+                    #region ExpressinNode
+                    {
+                        // #1 Code to Data
+                        // (query) => query.Where().OrderBy().Skip().Take().Select().ToList();
+                        node = convertService.ConvertToData(expression);
+
+                        // {"nodeType":"Lambda","parameterNames":["Param_1"],"body":{"nodeType":"Call","methodName":"Where","arguments":[{"parameterName":"Param_1","nodeType":"Member"},{"nodeType":"Lambda","parameterNames":["Param_0"],"body":{"left":{"nodeType":"Member","parameterName":"Param_0","memberName":"id"},"right":{"nodeType":"Constant","valueType":{"typeName":"Int32"},"value":10},"nodeType":"GreaterThanOrEqual"}}]}}
+                        var strNode = Json.Serialize(node);
+
+                        // #2 Data to Code
+                        // Param_1 => Param_1.Where(Param_0 => (Param_0.id >= 10))
+                        var lambdaExp = convertService.ToLambdaExpression(node, typeof(IQueryable<Person>));
+                        //var exp3 = (Expression<Func<IQueryable<Person>, IQueryable<Person>>>)lambdaExp;
+                        var predicate_ = lambdaExp.Compile();
+                    }
+                    #endregion
+
+                    // As Filter
+                    var queryAction = new QueryAction(node);
+                    var strQuery = Json.Serialize(queryAction);
+                    var predicate = convertService.ToPredicateExpression<Person>(queryAction.filter);
+                    //var lambdaExp = (Expression<Func<Person, bool>>)convertService.ToLambdaExpression(queryAction.filter, typeof(Person));
+
+
+                    var list = DataSource.GetQueryable().Where(predicate);
+
+
+                    list = list.OrderBy(queryAction.orders);
+
+                    var methodName = queryAction.method;
+
+                    if (methodName == "TotalCount") return list.Count();
+
+                    if (queryAction.skip.HasValue)
+                        list = list.Skip(queryAction.skip.Value);
+                    if (queryAction.take.HasValue)
+                        list = list.Take(queryAction.take.Value);
+
+                    switch (methodName)
+                    {
+                        case "First": return list.First();
+                        case "FirstOrDefault": return list.FirstOrDefault();
+                        case "Last": return list.Last();
+                        case "LastOrDefault": return list.LastOrDefault();
+                        case "Count": return list.Count();
+                        case "ToList":
+                        case "":
+                        case null:
+                            return list;
+                    }
+
+                    throw new NotSupportedException("Method not support:" + methodName);
+                };
+
+                var query = QueryableBuilder.Build<Person>(QueryExecutor);
+
+                var persons = new List<Person> { new Person { id = 2 }, new Person { id = 3 } }.AsQueryable();
+                var pids = persons.Select(m => m.id).ToList();
+
+
+                var jobs = new List<Job> { new Job { departmentId = 1 }, new Job { departmentId = 2 } };
+
+                query = query
+                #region work
+                        //.Where(m => m.job.name != "test")     // Member Access : Cascade
+                        //.Where(m => !pids.Contains(m.id))     // MethodCall : Contains
+                        .Where(m => (float)m.id >= 10.0)      // Convert
+                        //.Where(m => new List<int?> { 10 }.Contains(m.departmentId))        //  Nullable
+                        //.Where(m => new int?[] { 10 }.Contains(m.departmentId))            //  Array
+
+                        .Where(Param_0 => Param_0.id >= 10)
+                        //.Where(m => m.id < 20)
+                        //.Where(m => !m.name.Contains("8"))
+                        //.Where(m => !m.job.name.Contains("6"))
+                #endregion
+
+                        //.Where(m => persons.Any(p => p.id == m.id)) // MethodCall Any
+                        //.Where(m => persons.Where(p=>p.id>0).Any(p => p.id == m.id)) // MethodCall Any
+
+                        //.Where((m, index) => index > 10) //  MethodCall Where   not suppoerted in FilterAction
+
+                        //.OrderBy(m => m.job.name)
+                        //.OrderByDescending(m => m.id)
+                        //.ThenBy(m => m.job.departmentId)
+                        //.ThenByDescending(m => m.name)
+                        .Skip(1)
+                        .Take(5)
+                        ;
+
+
+                #region List
+                {
+                    var list = query.ToList();
+                    //Assert.AreEqual(5, list.Count);
+                    //Assert.AreEqual(17, list[0].id);
+                    //Assert.AreEqual(15, list[1].id);
+                }
+                #endregion
+
+                var count = query.Count();
+                var totalCount = query.TotalCount();
+
+                var First = query.First();
+                var FirstOrDefault = query.FirstOrDefault();
+                var Last = query.Last();
+                var LastOrDefault = query.LastOrDefault();
+
+            }
+
+
+        }
+
+
+
+
+    }
+}

+ 75 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/Serialize_Test.cs

@@ -0,0 +1,75 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.MsTest.New.test2
+{
+    [TestClass]
+    public class Serialize_Test
+    {
+        [TestMethod]
+        public void Test()
+        {
+            try
+            {
+                //var Json = Vit.Core.Module.Serialization.Json.Instance;
+                var Json = Sers.Core.Module.Serialization.Text.Serialization_Text.Instance;
+
+                var service = ExpressionConvertService.Instance;
+
+                Expression<Func<Person, bool>> predicate = person => person.id > 0;
+
+                Expression expression = predicate;
+                ExpressionNode_New node = service.ConvertToData(expression);
+
+                var str = Json.Serialize(node);
+
+                var node2 = Json.Deserialize<ExpressionNode>(str);
+                var str2 = Json.Serialize(node2);
+
+            }
+            catch (Exception ex)
+            {
+                throw;
+            }
+
+            try
+            {
+                var Json = Vit.Core.Module.Serialization.Json.Instance;
+               
+                var service = ExpressionConvertService.Instance;
+
+                Expression<Func<Person, bool>> predicate = person => person.id > 0;
+
+                Expression expression = predicate;
+                ExpressionNode_New node = service.ConvertToData(expression);
+
+                var str = Json.Serialize(node);
+
+                var node2 = Json.Deserialize<ExpressionNode>(str);
+                var str2 = Json.Serialize(node2);
+
+            }
+            catch (Exception ex)
+            {
+                throw;
+            }
+
+
+
+
+
+
+
+
+
+
+        }
+
+
+    }
+}

+ 359 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/ExpressionTree/ValueType_Test.cs

@@ -0,0 +1,359 @@
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+using ValueType = Vit.Linq.ExpressionTree.ComponentModel.ValueType;
+
+namespace Vit.Linq.MsTest.Converter
+{
+    [TestClass]
+    public class ValueType_Test
+    {
+        [TestMethod]
+        public void ConvertToPrimitiveType_Test()
+        {
+
+            #region ValueType
+            // int
+            {
+                var type = typeof(int);
+                int expectedValue = 12;
+                object value;
+
+                value = ValueType.ConvertToPrimitiveType(12.0, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType(12.1, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType(12, type);
+                Assert.AreEqual(expectedValue, value);
+
+
+                value = ValueType.ConvertToPrimitiveType("12", type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("12.0", type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("12.1", type);
+                Assert.AreEqual(expectedValue, value);
+
+                // nullable
+                value = ValueType.ConvertToPrimitiveType((int?)null, type);
+                Assert.AreEqual(0, value);
+
+                value = ValueType.ConvertToPrimitiveType((int?)12, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType((double?)12.0, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType((double?)12.1, type);
+                Assert.AreEqual(expectedValue, value);
+
+            }
+            // int2
+            {
+                var type = typeof(int);
+                int expectedValue = -12;
+
+                var value = ValueType.ConvertToPrimitiveType(-12.1, type);
+                Assert.AreEqual(expectedValue, value);
+            }
+            // byte
+            {
+                var type = typeof(byte);
+                byte expectedValue = 12;
+                object value;
+
+
+                value = ValueType.ConvertToPrimitiveType(12.0, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType(12.1, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType(12, type);
+                Assert.AreEqual(expectedValue, value);
+
+
+                value = ValueType.ConvertToPrimitiveType("12", type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("12.0", type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("12.1", type);
+                Assert.AreEqual(expectedValue, value);
+            }
+            // float
+            {
+                var type = typeof(float);
+                float expectedValue = 12.123f;
+                object value;
+
+                value = ValueType.ConvertToPrimitiveType(12.123, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("12.123", type);
+                Assert.AreEqual(expectedValue, value);
+
+
+
+                expectedValue = (float)12;
+                value = ValueType.ConvertToPrimitiveType(12, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("12", type);
+                Assert.AreEqual(expectedValue, value);
+            }
+
+
+            // bool
+            {
+                var type = typeof(bool);
+                bool expectedValue = true;
+                object value;
+
+                value = ValueType.ConvertToPrimitiveType(true, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("true", type);
+                Assert.AreEqual(expectedValue, value);
+
+
+                expectedValue = false;
+                value = ValueType.ConvertToPrimitiveType("false", type);
+                Assert.AreEqual(expectedValue, value);
+            }
+
+            // DateTime
+            {
+                var type = typeof(DateTime);
+                object value;
+
+                var strDate = "2001-02-03 04:05:06";
+                var date = DateTime.Parse(strDate);
+
+                value = ValueType.ConvertToPrimitiveType(strDate, type);
+                Assert.AreEqual(date, value);
+
+                value = ValueType.ConvertToPrimitiveType((DateTime?)date, type);
+                Assert.AreEqual(date, value);
+
+                value = ValueType.ConvertToPrimitiveType((DateTime?)null, type);
+                Assert.AreEqual(DateTime.MinValue, value);
+
+                value = ValueType.ConvertToPrimitiveType("2001-02-03T04:05:06Z", type);
+                value = ((DateTime)value).ToUniversalTime();
+                Assert.AreEqual(date, value);
+            }
+
+
+            // string
+            {
+                var type = typeof(string);
+                object value;
+
+                // ##1 string -> string
+                value = ValueType.ConvertToPrimitiveType("trueasdge", type);
+                Assert.AreEqual("trueasdge", value);
+
+
+                // ##2 bool -> string
+                value = ValueType.ConvertToPrimitiveType(true, type);
+                Assert.AreEqual("true", value);
+
+                value = ValueType.ConvertToPrimitiveType(false, type);
+                Assert.AreEqual("false", value);
+
+                value = ValueType.ConvertToPrimitiveType((bool?)true, type);
+                Assert.AreEqual("true", value);
+
+                value = ValueType.ConvertToPrimitiveType((bool?)null, type);
+                Assert.AreEqual(null, value);
+
+
+                // ##3 double -> string
+                value = ValueType.ConvertToPrimitiveType(12.1, type);
+                Assert.AreEqual("12.1", value);
+
+                value = ValueType.ConvertToPrimitiveType(12, type);
+                Assert.AreEqual("12", value);
+
+
+                value = ValueType.ConvertToPrimitiveType((int?)null, type);
+                Assert.AreEqual(null, value);
+            }
+            #endregion
+
+
+
+        }
+
+
+        [TestMethod]
+        public void ConvertToType_Nullable_Test()
+        {
+            Type type;
+            object value;
+            object expectedValue;
+
+            #region Nullable
+            // int
+            {
+                type = typeof(int?);
+                expectedValue = (int?)12;
+                int? v;
+
+                value = ValueType.ConvertToType(12.0, type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                value = ValueType.ConvertToType(12.1, type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                value = ValueType.ConvertToType(12, type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+
+                value = ValueType.ConvertToType("12", type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                value = ValueType.ConvertToType("12.0", type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                value = ValueType.ConvertToType("12.1", type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                // nullable
+                value = ValueType.ConvertToType((int?)null, type);
+                v = value as int?;
+                Assert.AreEqual(null, v);
+
+                value = ValueType.ConvertToType((int?)12, type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                value = ValueType.ConvertToType((double?)12.0, type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+                value = ValueType.ConvertToType((double?)12.1, type);
+                v = value as int?;
+                Assert.AreEqual(expectedValue, v);
+
+            }
+
+            // bool
+            {
+                type = typeof(bool?);
+                expectedValue = (bool?)true;
+
+                value = ValueType.ConvertToType(true, type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToType("true", type);
+                Assert.AreEqual(expectedValue, value);
+
+                value = ValueType.ConvertToType(null, type);
+                Assert.AreEqual((bool?)null, value);
+
+                value = ValueType.ConvertToType("false", type);
+                Assert.AreEqual((bool?)false, value);
+            }
+            #endregion
+        }
+
+
+        [TestMethod]
+        public void ConvertToType_Array_Test()
+        {
+
+            #region One-dimensional Array
+            // int []
+            {
+                var type = typeof(int[]);
+                int expectedValue = 12;
+
+                var value = ValueType.ConvertToType(new[] { 12.1 }, type) as int[];
+                Assert.AreEqual(expectedValue, value[0]);
+            }
+
+            // int? []
+            {
+                var type = typeof(int?[]);
+                int? expectedValue = 12;
+
+                var value = ValueType.ConvertToType(new[] { 12.1 }, type) as int?[];
+                Assert.AreEqual(expectedValue, value[0]);
+            }
+            #endregion
+
+
+            #region One-dimensional Collection
+            // double[]  ->  List<int>
+            {
+                var type = typeof(List<int>);
+                int expectedValue = 12;
+
+                var value = ValueType.ConvertToType(new[] { 12.1 }, type) as List<int>;
+                Assert.AreEqual(expectedValue, value[0]);
+            }
+            // IEnumerable<double>  ->  IEnumerable<int>
+            {
+                var type = typeof(IEnumerable<int>);
+                int expectedValue = 12;
+                IEnumerable<double> oriValue = new[] { 11.1 }.Select(m => m + 1);
+
+                var value = ValueType.ConvertToType(oriValue, type) as IEnumerable<int>;
+                Assert.AreEqual(expectedValue, value.First());
+            }
+            // IEnumerable<double>  ->  IQueryable<int>
+            {
+                var type = typeof(IQueryable<int>);
+                int expectedValue = 12;
+                IEnumerable<double> oriValue = new[] { 11.1 }.Select(m => m + 1);
+
+                var value = ValueType.ConvertToType(oriValue, type) as IQueryable<int>;
+                Assert.AreEqual(expectedValue, value.First());
+            }
+            // IQueryable<double>  ->  ICollection<string>
+            {
+                var type = typeof(ICollection<string>);
+                var expectedValue = "12.1";
+                IQueryable<double> oriValue = new[] { 11.1 }.AsQueryable().Select(m => m + 1);
+
+                var value = ValueType.ConvertToType(oriValue, type) as ICollection<string>;
+                Assert.AreEqual(expectedValue, value.First());
+            }
+            #endregion
+
+
+            #region Two-dimensional
+            // double [][] -> int [][]
+            {
+                var type = typeof(int[][]);
+                int expectedValue = 12;
+
+                var value = ValueType.ConvertToType(new[] { new[] { 12.1 } }, type) as int[][];
+                Assert.AreEqual(expectedValue, value[0][0]);
+            }
+            #endregion
+        }
+
+ 
+
+
+    }
+}

+ 50 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/README.md

@@ -0,0 +1,50 @@
+
+# TODO: 
+
+
+# CollectionStream. Select
+TODO: .OrderBy().Skip().Take()
+
+# join
+select u.id,u.name, u.birth,u.fatherId ,u.motherId, father.name,mother.name
+from `User` u
+left join `User` father on u.fatherId = father.id 
+inner join `User` mother on u.motherId = mother.id
+where u.id>1 
+limit 1,5;
+
+# group by
+select WarpWarehouseID 
+from location l where WarpWarehouseID !=2300
+group by l.WarpWarehouseID having WarpWarehouseID>10
+
+
+# QueryAction
+
+# LeftJoin
+# InnerJoin
+
+# SelectMany
+
+# GroupBy 
+
+
+# support ExpressionType.Quote
+
+# Deserialize
+
+
+-------------
+release note: 
+
+# Join 
+# Select
+
+
+# fix parameterName duplicate issue
+# try convert constant value as parameters
+
+# Any
+      var persons = new List<Person> { new Person { id = 2 }, new Person { id = 3 } }.AsQueryable();
+                    query = query
+                        .Where(m => persons.Any(p => p.id == m.id))

+ 28 - 0
src/Test/Vit.Linq.ExpressionTree.MsTest/Vit.Linq.ExpressionTree.MsTest.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <Compile Remove="ExpressionTree\Any_Test.cs" />
+      <Compile Remove="ExpressionTree\GroupBy_Test.cs" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
+        <PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
+        
+        <PackageReference Include="Vit.Core" Version="2.1.21" />
+        <PackageReference Include="Sers.Core" Version="2.1.24" />
+
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\..\Vit.Linq\Vit.Linq.csproj" />
+    </ItemGroup>
+
+</Project>

+ 0 - 1
src/Test/Vit.Linq.MsTest/Vit.Linq.MsTest.csproj

@@ -2,7 +2,6 @@
 
     <PropertyGroup>
         <TargetFramework>net6.0</TargetFramework>
-
         <IsPackable>false</IsPackable>
         <IsTestProject>true</IsTestProject>
     </PropertyGroup>

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

@@ -0,0 +1,55 @@
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+using Vit.Orm;
+using Vit.Orm.Sqlite.MsTest;
+
+namespace Vit.Linq.MsTest.Converter
+{
+
+    [TestClass]
+    public class Query_Test
+    {
+        DbContext GetDbContext() => TestData.BuildInitedDatabase(GetType().Name);
+
+        [TestMethod]
+        public void Test_SimpleQuery()
+        {
+            var dbContext = GetDbContext();
+
+            if (1 == 1)
+            {
+                var users = dbContext.Query<User>();
+                {
+                    var userList = users.ToList();
+                    Assert.AreEqual(6, userList.Count);
+                }
+            }
+
+            if (1 == 1)
+            {
+                var users = dbContext.Query<User>();
+                {
+                    var userList = users.Where(u => u.id > 2).Where(m => m.fatherId == 5).ToList();
+                    Assert.AreEqual(3, userList[0].id);
+                }
+                {
+                    var userList = users.Where(u => u.id + 1 == 4).Where(m => m.fatherId == 5).ToList();
+                    Assert.AreEqual(3, userList[0].id);
+                }
+                {
+                    var userList = users.Where(u => 4 == u.id + 1).Where(m => m.fatherId == 5).ToList();
+                    Assert.AreEqual(3, userList[0].id);
+                }
+
+                {
+                    var userList = users.Where(u => u.birth >= new DateTime(2021, 01, 03)).ToList();
+                    Assert.AreEqual(3, userList.Count);
+                }
+            }
+
+        }
+ 
+    }
+}

+ 287 - 0
src/Test/Vit.Orm.Sqlite.MsTest/Queryable_Test.cs

@@ -0,0 +1,287 @@
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Extensions.Linq_Extensions;
+using System.Data;
+using Vit.Orm;
+using Dapper.Contrib.Extensions;
+using Vit.Orm.Sqlite.Extensions;
+
+namespace Vit.Linq.MsTest.Converter
+{
+
+
+
+
+
+
+    [Table("User")]
+    public class User
+    {
+        [Key]
+        //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+        public int id { get; set; }
+        public string name { get; set; }
+        public DateTime? birth { get; set; }
+
+        public int? fatherId { get; set; }
+        public int? motherId { get; set; }
+    }
+
+
+
+    [TestClass]
+    public class Queryable_Test
+    {
+        static DbContext GetDbContext()
+        {
+            var dbContext = new DbContext();
+            dbContext.UseSqlite("data source=T:\\sample\\sers\\sqlite.db");
+
+            return dbContext;
+        }
+
+
+        [TestMethod]
+        public void Test_Insert()
+        {
+            var dbContext = GetDbContext();
+
+            #region ## Insert
+            if (1 == 1)
+            {
+                var user = new User { id = 7, name = "testUser", birth = DateTime.Now, fatherId = 1, motherId = 2 };
+
+                dbContext.Insert(user);
+
+                Assert.IsTrue(user.id > 0);
+            }
+            #endregion
+
+        }
+
+        [TestMethod]
+        public void Test_Update()
+        {
+            var dbContext = GetDbContext();
+
+            #region ## Test_Update
+            if (1 == 1)
+            {
+                var user = new User { id = 7, name = "testUser2", birth = DateTime.Now, fatherId = 1, motherId = 2 };
+
+                var rowCount=dbContext.Update(user);
+
+                Assert.AreEqual(1, rowCount);
+            }
+            #endregion
+        }
+
+        [TestMethod]
+        public void Test_Delete()
+        {
+            var dbContext = GetDbContext();
+
+            if (1 == 1)
+            {
+                var user = new User { id = 7, name = "testUser2", birth = DateTime.Now, fatherId = 1, motherId = 2 };
+
+                var rowCount = dbContext.Delete(user);
+
+                Assert.AreEqual(1, rowCount);
+            }
+            if (1 == 1)
+            {
+                var rowCount = dbContext.DeleteByKey<User>(7);
+                Assert.AreEqual(0, rowCount);
+            }
+        
+        }
+
+
+
+        [TestMethod]
+        public void Test_Query()
+        {
+
+            var dbContext = GetDbContext();
+
+
+            #region ## Count
+            if (1 == 2)
+            {
+                var users = dbContext.Query<User>();
+
+                var count = (from user in users
+                             from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             where user.id > 2 && father == null
+                             select new
+                             {
+                                 father
+                             }).Count();
+
+                Assert.AreEqual(3, count);
+            }
+            #endregion
+
+
+            #region ##  OrderBy Skip/Take ToSql
+            if (1 == 1)
+            {
+                var users = dbContext.Query<User>();
+
+                var query = (from user in users
+                             from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             where user.id > 2
+                             orderby father.id, user.id descending
+                             select new
+                             {
+                                 user,
+                                 father
+                             })
+                            .Skip(1).Take(2);
+
+                var sql = query.ToSql();
+                var list = query.ToList();
+
+                Assert.AreEqual(2, list.Count);
+                Assert.AreEqual(5, list[0].user.id);
+                Assert.AreEqual(4, list[1].user.id);
+            }
+            #endregion
+           
+
+            #region #1 Join
+            // worked
+            if (1 == 1)
+            {
+                var users = dbContext.Query<User>();
+
+                var userList = (from user in users
+                                from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                                where user.id > 2 && father == null
+                                select new
+                                {
+                                    userId = user.id + 100,
+                                    user,
+                                    father,
+                                    hasFather = father != null,
+                                    fatherName = father.name,
+                                }).ToList();
+            }
+            if (1 == 2)
+            {
+                var users = dbContext.Query<User>();
+                var userList = (from user in users
+                                from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                                from mother in users.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
+                                select new
+                                {
+                                    userId = user.id + 100,
+                                    hasFather = user.fatherId != null ? true : false,
+                                    fatherName = father.name,
+                                    motherName = mother.name,
+                                }).ToList();
+
+            }
+            if (1 == 2)
+            {
+                var users = dbContext.Query<User>();
+                var userList = (from user in users
+                                    from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                                    from mother in users.Where(mother => user.motherId == mother.id).DefaultIfEmpty()
+                                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
+                                }).ToList();
+            }
+
+            if (1 == 2)
+            {
+                var users = dbContext.Query<User>();
+                var userList = (from user in users
+                                select new
+                                {
+                                    uniqueId1 = user.id + "_" + user.fatherId + "_" + user.motherId,
+                                    uniqueId2 = $"{ user.id }_{ user.fatherId }_{ user.motherId }"
+                                }).ToList();
+            }
+
+            #endregion
+
+           
+
+
+
+        }
+
+        [TestMethod]
+        public void Test_ExecuteUpdate()
+        {
+
+            var dbContext = GetDbContext();
+
+
+            if (1 == 1)
+            {
+                var users = dbContext.Query<User>();
+
+                var count = users.ExecuteUpdate(row => new User
+                {
+                    name = "u_" + row.id + "_" + (row.fatherId.ToString() ?? "") + "_" + (row.motherId.ToString() ?? "")
+                });
+
+                Assert.AreEqual(6, count);
+            }
+
+            if (1 == 3)
+            {
+                var users = dbContext.Query<User>();
+
+                var query = (from user in users
+                             from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             select new
+                             {
+                                 user,
+                                 father
+                             });
+
+                var count = query.ExecuteUpdate(row => new User
+                {
+                    name = "u_" + row.user.id + "_" + (row.father.id.ToString() ?? "") + "|" + (row.user.name ?? "")
+                });
+
+                Assert.AreEqual(3, count);
+            }
+
+
+
+            if (1 == 2)
+            {
+                var users = dbContext.Query<User>();
+
+                var query = (from user in users
+                             from father in users.Where(father => user.fatherId == father.id).DefaultIfEmpty()
+                             where user.id <= 5 && father != null
+                             select new
+                             {
+                                 user,
+                                 father
+                             });
+
+                var count = query.ExecuteUpdate(row => new User { name = row.father.name });
+
+                Assert.AreEqual(3, count);
+            }
+
+
+        }
+    }
+}

+ 52 - 0
src/Test/Vit.Orm.Sqlite.MsTest/TestData.cs

@@ -0,0 +1,52 @@
+using Dapper.Contrib.Extensions;
+
+using Vit.Orm.Sqlite.Extensions;
+
+namespace Vit.Orm.Sqlite.MsTest
+{
+    [Table("User")]
+    public class User
+    {
+        [Key]
+        //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+        public int id { get; set; }
+        public string name { get; set; }
+        public DateTime? birth { get; set; }
+
+        public int? fatherId { get; set; }
+        public int? motherId { get; set; }
+    }
+
+
+    public class TestData
+    {
+
+        public static DbContext BuildInitedDatabase(string dbName)
+        {
+            //"data source=T:\\sample\\sers\\sqlite.db"
+            var filePath = $"T:\\sample\\sers\\{dbName}.db";
+            if (File.Exists(filePath)) File.Delete(filePath);
+
+            File.WriteAllBytes(filePath, new byte[0]);
+
+            var dbContext = new DbContext();
+            dbContext.UseSqlite($"data source={filePath}");
+
+            var dbSet = dbContext.DbSet<User>();
+            dbSet.Create();
+
+            new List<User> {
+                    new User {id=1,name="u1",fatherId=5,motherId=6 },
+                    new User {id=2,name="u2",fatherId=5,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"},
+
+                }.ForEach(user => dbSet.Insert(user));
+
+            return dbContext;
+        }
+
+    }
+}

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

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
+        <PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\..\Vit.Orm.Sqlite\Vit.Orm.Sqlite.csproj" />
+    </ItemGroup>
+
+</Project>

+ 46 - 0
src/Vit.Linq/ExpressionTree/CodeConvertArgument.cs

@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree
+{
+    public class CodeConvertArgument
+    {
+        private CodeConvertArgument()
+        {
+        }
+
+        public ExpressionConvertService convertService { get; set; }
+        public List<ParameterExpression> parameters;
+
+        public Func<ExpressionNode_New, Type> getResultTypeForNewNode;
+
+        public static CodeConvertArgument WithParams(ExpressionConvertService convertService, List<ParameterExpression> parameters = null, Func<ExpressionNode_New, Type> getResultTypeForNewNode = null)
+        {
+            return new CodeConvertArgument { convertService = convertService, parameters = parameters, getResultTypeForNewNode = getResultTypeForNewNode };
+        }
+
+        public CodeConvertArgument WithParams(params ParameterExpression[] newParams)
+        {
+            var arg = new CodeConvertArgument { convertService = convertService, getResultTypeForNewNode = getResultTypeForNewNode };
+
+            if (parameters?.Any() == true)
+            {
+                arg.parameters = parameters.ToList();
+            }
+            else
+            {
+                arg.parameters = new List<ParameterExpression>();
+            }
+
+            if (newParams.Any())
+            {
+                arg.parameters.AddRange(newParams);
+            }
+            return arg;
+        }
+    }
+}

+ 183 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/CollectionQuery/QueryAction.LoadFromNode.cs

@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+
+
+namespace Vit.Linq.ExpressionTree.ComponentModel.CollectionQuery
+{
+    public partial class QueryAction
+    {
+        class ConvertArgument
+        {
+            public QueryAction queryAction;
+            public string parameterName;
+            public bool _gettedOrder = false;
+
+            public ExpressionNodeCloner cloner;
+        }
+
+        public static void LoadFromNode(QueryAction queryAction, ExpressionNode_Lambda lambda)
+        {
+            var arg = new ConvertArgument { queryAction = queryAction, parameterName = "m" };
+
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) => (true, ConvertFilter(arg, node));
+            arg.cloner = cloner;
+
+            var filter = ConvertFilter(arg, lambda.body);
+            queryAction.filter = ExpressionNode.Lambda(parameterNames: new[] { arg.parameterName }, body: filter);
+        }
+
+        static ExpressionNode ConvertFilter(ConvertArgument arg, ExpressionNode node)
+        {
+            var dest = ConvertFilter(arg, node, out var success);
+
+            if (success) return dest;
+
+            return arg.cloner.Clone(node);
+        }
+
+        static ExpressionNode ConvertFilter(ConvertArgument arg, ExpressionNode node, out bool success)
+        {
+            success = true;
+            if (node == null) return null;
+
+            if (node.nodeType == NodeType.Lambda)
+            {
+                ExpressionNode_Lambda lambda = node;
+                if (lambda.parameterNames?.Length == 1)
+                    return ConvertFilter(arg, lambda.body);
+            }
+
+            if (node.nodeType == NodeType.Member)
+            {
+                ExpressionNode_Member member = node;
+
+                var parameterName = string.IsNullOrWhiteSpace(member.parameterName) ? null : arg.parameterName;
+                var objectValue = member.objectValue;
+                if (objectValue != null)
+                {
+                    objectValue = ConvertFilter(arg, objectValue);
+                }
+                return ExpressionNode.Member(objectValue: objectValue, memberName: member.memberName, parameterName: parameterName);
+            }
+
+            #region ExpressionNode_Call
+            if (node.nodeType == NodeType.MethodCall)
+            {
+                ExpressionNode_MethodCall call = node;
+                switch (call.methodName)
+                {
+                    case "Take":
+                        {
+                            arg.queryAction.take = (call.arguments[1] as ExpressionNode_Constant)?.value as int?;
+                            return ConvertFilter(arg, call.arguments[0]);
+                        }
+
+                    case "Skip":
+                        {
+                            arg.queryAction.skip = (call.arguments[1] as ExpressionNode_Constant)?.value as int?;
+                            return ConvertFilter(arg, call.arguments[0]);
+                        }
+
+                    case "OrderBy":
+                    case "OrderByDescending":
+                    case "ThenBy":
+                    case "ThenByDescending":
+                        {
+                            if (!arg._gettedOrder)
+                            {
+                                var methodName = call.methodName;
+
+                                var memberField = call.arguments[1];
+
+                                var orderParam = new SortField { member = memberField, asc = !methodName.EndsWith("Descending") };
+
+                                if (methodName.StartsWith("Order"))
+                                {
+                                    arg._gettedOrder = true;
+                                }
+
+                                if (arg.queryAction.orders == null)
+                                    arg.queryAction.orders = new List<SortField> { orderParam };
+                                else
+                                    arg.queryAction.orders.Insert(0, orderParam);
+
+                            }
+                            return ConvertFilter(arg, call.arguments[0]);
+                        }
+                    case "Where":
+                        {
+                            var source = call.arguments[0];
+                            var predicate = call.arguments[1];
+                            var right = ConvertFilter(arg, predicate);
+                            if (right.nodeType == NodeType.Lambda)
+                            {
+                                ExpressionNode_Lambda lambda = right;
+                                if (lambda.parameterNames?.Length == 1)
+                                {
+                                    right = lambda.body;
+                                }
+                                else
+                                {
+                                    throw new Exception("not supported Where filter with index");
+                                }
+                            }
+
+                            if (source == null || (source.nodeType == NodeType.Member && source.memberName == null && source.parameterName == null))
+                            {
+                                return right;
+                            }
+
+                            var left = ConvertFilter(arg, source);
+                            if (left == null || left.nodeType == NodeType.Member) return right;
+                            if (left.nodeType == NodeType.Lambda)
+                            {
+                                ExpressionNode_Lambda lambda2 = left;
+                                if (lambda2.parameterNames?.Length == 1)
+                                {
+                                    left = lambda2.body;
+                                }
+                                else
+                                {
+                                    throw new Exception("not supported Where filter with index");
+                                }
+                            }
+                            return ExpressionNode.And(left: left, right: right);
+                        }
+                    case "TotalCount":
+                    case "First":
+                    case "FirstOrDefault":
+                    case "Last":
+                    case "LastOrDefault":
+                    case "Count":
+                        {
+                            if (!string.IsNullOrWhiteSpace(arg.queryAction.method)) throw new Exception("can not process multiple Method call");
+
+                            arg.queryAction.method = call.methodName;
+                            return ConvertFilter(arg, call.arguments[0]);
+                        }
+
+                    case "IsNullOrEmpty":
+                    case "ElementAt":
+
+                    case "Contains":
+                    case "Any":
+                        {
+                            return node;
+                        }
+                }
+                throw new Exception("not supported method call : " + call.methodName);
+            }
+            #endregion
+
+            success = false;
+            return null;
+        }
+
+
+
+    }
+}

+ 36 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/CollectionQuery/QueryAction.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+ 
+
+namespace Vit.Linq.ExpressionTree.ComponentModel.CollectionQuery
+{
+    public partial class QueryAction
+    {
+        public QueryAction() { }
+
+
+        public QueryAction(ExpressionNode node)
+        {
+            LoadFromNode(this, node);
+        }
+
+
+        public ExpressionNode filter { get; set; }
+
+        public List<SortField> orders { get; set; }
+
+        public int? skip { get; set; }
+        public int? take { get; set; }
+
+
+        /// <summary>
+        /// default is ToList, could be :  Count | First | FirstOrDefault | Last | LastOrDefault | TotalCount
+        /// </summary>
+        public string method { get; set; }
+
+
+    }
+}

+ 470 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/CollectionsQuery/StreamReader.cs

@@ -0,0 +1,470 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Vit.Linq.ExpressionTree.ComponentModel.CollectionsQuery
+{
+
+    class ExpressionNode_RenameableMember : ExpressionNode
+    {
+        private IStream stream;
+        public override string parameterName
+        {
+            get => stream?.alias;
+            set => throw new NotSupportedException();
+        }
+        public static ExpressionNode Member(IStream stream, string memberName, ExpressionNode_Member member)
+        {
+            var node = new ExpressionNode_RenameableMember
+            {
+                nodeType = NodeType.Member,
+                stream = stream,
+                memberName = memberName,
+            };
+            node.Member_SetType(member.Member_GetType());
+            return node;
+        }
+    }
+
+    class Argument
+    {
+        public Dictionary<string, IStream> streamAliasMap;
+
+        public Argument WithAlias(IStream stream, string alias)
+        {
+            var arg = new Argument();
+
+            var map = streamAliasMap ?? new Dictionary<string, IStream>();
+            arg.streamAliasMap = map.ToDictionary(kv => kv.Key, kv => kv.Value);
+            arg.streamAliasMap[alias] = stream;
+            return arg;
+        }
+    }
+
+    public class StreamReader
+    {
+
+        /// <summary>
+        /// lambda:
+        ///     (query,query2) => query.SelectMany(query2).Where().OrderBy().Skip().Take().Select().ToList();
+        /// </summary>
+        /// <param name="lambda"> </param>
+        /// <returns> </returns>
+        public static IStream ReadNode(ExpressionNode_Lambda lambda)
+        {
+            return new StreamReader().ReadFromNode(lambda);
+        }
+
+
+        /// <summary>
+        /// lambda:
+        ///     (query,query2) => query.SelectMany(query2).Where().OrderBy().Skip().Take().Select().ToList();
+        /// </summary>
+        /// <param name="lambda"> </param>
+        /// <returns> </returns>
+        public IStream ReadFromNode(ExpressionNode_Lambda lambda)
+        {
+            var arg = new Argument();
+            return ReadStream(arg, lambda.body);
+        }
+        int aliasNameCount = 0;
+        string NewAliasName()
+        {
+            return "t" + (aliasNameCount++);
+        }
+
+        // query.SelectMany(query2).Where().OrderBy().Skip().Take().Select()
+        IStream ReadStream(Argument arg, ExpressionNode node)
+        {
+            switch (node.nodeType)
+            {
+                case NodeType.Member:
+                    {
+                        ExpressionNode_Member member = node;
+                        var oriValue = member.Member_GetOriValue();
+                        if (oriValue != null)
+                            return new SourceStream(oriValue, NewAliasName());
+                        break;
+                    }
+                case NodeType.Constant:
+                    {
+                        ExpressionNode_Constant constant = node;
+                        var oriValue = constant.value;
+                        return new SourceStream(oriValue, NewAliasName());
+                    }
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall call = node;
+                        switch (call.methodName)
+                        {
+                            case "SelectMany":
+                                {
+                                    var left = ReadStream(arg, call.arguments[0]);
+                                    ExpressionNode_Lambda rightSelector = call.arguments[1];
+                                    ExpressionNode_Lambda resultSelector = call.arguments[2];
+                                    return SelectMany(arg, left, rightSelector, resultSelector);
+                                }
+                            case "Where":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    var predicateLambda = call.arguments[1] as ExpressionNode_Lambda;
+                                    ExpressionNode where;
+
+                                    if (source is JoinedStream joinedStream)
+                                    {
+                                        if (  joinedStream.orders == null
+                                            && joinedStream.skip == null && joinedStream.take == null
+                                            && joinedStream.select?.existCalculatedField != true)
+                                        {
+                                            where = ReadWhere(arg, joinedStream, predicateLambda);
+                                            joinedStream.where = joinedStream.where == null ? where : ExpressionNode.And(left: joinedStream.where, right: where);
+                                            return joinedStream;
+                                        }
+                                    }
+
+                                    where = ReadWhere(arg, source, predicateLambda);
+                                    joinedStream = new JoinedStream(NewAliasName()) { left = source };
+                                    joinedStream.where = where;
+                                    return joinedStream;
+                                }
+                            case "Select":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    ExpressionNode_Lambda resultSelector = call.arguments[1];
+
+                                    var select = ReadFieldSelect(resultSelector, source);
+
+                                    if (source is JoinedStream joinedStream && joinedStream.select == null)
+                                    {
+                                        joinedStream.select = select;
+                                    }
+                                    else
+                                    {
+                                        source = new JoinedStream(NewAliasName()) { left = source, select = select };
+                                    }
+                                    return source;
+                                }
+                            case "ExecuteUpdate":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    ExpressionNode_Lambda resultSelector = call.arguments[1];
+
+                                    var select = ReadFieldSelect(resultSelector, source);
+
+                                    return new StreamToUpdate(source) { fieldsToUpdate = select.fields };
+                                }
+                            case "Take":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    JoinedStream joinedStream = source as JoinedStream;
+                                    if (joinedStream == null)
+                                    {
+                                        joinedStream = new JoinedStream(NewAliasName()) { left = source };
+                                    }
+
+                                    joinedStream.take = (call.arguments[1] as ExpressionNode_Constant)?.value as int?;
+                                    return joinedStream;
+                                }
+                            case "Skip":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    JoinedStream joinedStream = source as JoinedStream;
+                                    if (joinedStream == null)
+                                    {
+                                        joinedStream = new JoinedStream(NewAliasName()) { left = source };
+                                    }
+
+                                    joinedStream.skip = (call.arguments[1] as ExpressionNode_Constant)?.value as int?;
+                                    return joinedStream;
+                                }
+
+                            case "OrderBy":
+                            case "OrderByDescending":
+                            case "ThenBy":
+                            case "ThenByDescending":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    JoinedStream joinedStream = source as JoinedStream;
+                                    if (joinedStream == null)
+                                    {
+                                        joinedStream = new JoinedStream(NewAliasName()) { left = source };
+                                    }
+
+
+                                    var methodName = call.methodName;
+
+                                    var memberField = ReadSortField(call.arguments[1], source);
+
+                                    var orderParam = new SortField { member = memberField, asc = !methodName.EndsWith("Descending") };
+
+                                    if (methodName.StartsWith("OrderBy"))
+                                    {
+                                        joinedStream.orders = new List<SortField>();
+                                    }
+
+                                    joinedStream.orders ??= new List<SortField>();
+
+                                    joinedStream.orders.Add(orderParam);
+
+                                    return joinedStream;
+                                }
+                            case "Count":
+                            case "ToSql":
+                                {
+                                    if (call.arguments?.Length != 1) break;
+
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    JoinedStream joinedStream = source as JoinedStream;
+                                    if (joinedStream == null)
+                                    {
+                                        joinedStream = new JoinedStream(NewAliasName()) { left = source };
+                                    }
+
+                                    joinedStream.method = call.methodName;
+                                    return joinedStream;
+                                }
+                        }
+                        throw new Exception("[CollectionStream] unexpected method call : " + call.methodName);
+                    }
+            }
+            throw new NotSupportedException($"[CollectionStream] unexpected expression : {node.nodeType}");
+        }
+
+
+        // users.SelectMany(
+        //      user => users.Where(father => (father.id == user.fatherId)).DefaultIfEmpty(),
+        //      (user, father) => new <>f__AnonymousType4`2(user = user, father = father)
+        //  )
+        IStream SelectMany(Argument arg, IStream left, ExpressionNode_Lambda rightSelector, ExpressionNode_Lambda resultSelector)
+        {
+            // #1 right stream
+            var right = ReadRightStream(arg, left, rightSelector);
+
+            // #2 select
+            // (user, father) => new <>f__AnonymousType4`2(user = user, father = father)
+            var select = ReadFieldSelect(resultSelector, left, right.right);
+
+
+            // #3 merge multiple join
+            if (left is JoinedStream joinedStream)
+            {
+                if ( joinedStream.where == null && joinedStream.orders == null
+                    && joinedStream.skip == null && joinedStream.take == null
+                    && joinedStream.select?.existCalculatedField != true)
+                {
+                    joinedStream.joins ??= new List<StreamToJoin>();
+                    joinedStream.joins.Add(right);
+                    joinedStream.select = select;
+                    return joinedStream;
+                }
+                throw new NotSupportedException($"[CollectionStream] not support inner select in join sentence");
+            }
+
+            var stream = new JoinedStream(NewAliasName());
+            stream.left = left;
+            stream.joins = new List<StreamToJoin> { right };
+            stream.select = select;
+            return stream;
+
+
+            #region method for SelectMany
+
+            // rightSelector:
+            //      user => users.Where(father => (father.id == user.fatherId)).DefaultIfEmpty(),
+            StreamToJoin ReadRightStream(Argument arg, IStream left, ExpressionNode_Lambda rightSelector)
+            {
+                string joinType = "innerJoin";
+                IStream right = null;
+                ExpressionNode on = null;
+
+                ReadNode(arg.WithAlias(left, rightSelector.parameterNames[0]), rightSelector.body);
+                var rightStream = new StreamToJoin();
+                rightStream.joinType = joinType;
+                rightStream.right = right;
+                rightStream.on = on;
+
+                return rightStream;
+
+                void ReadNode(Argument arg, ExpressionNode node)
+                {
+                    if (node.nodeType != NodeType.MethodCall)
+                        throw new NotSupportedException($"[CollectionStream] unexpected expression : {node.nodeType}");
+
+                    ExpressionNode_MethodCall call = node;
+                    switch (call.methodName)
+                    {
+                        case "Where":
+                            {
+                                if (on != null)
+                                    throw new Exception("[CollectionStream] unexpected multiple where in join");
+
+                                var source = ReadStream(arg, call.arguments[0]);
+                                var predicateLambda = call.arguments[1] as ExpressionNode_Lambda;
+
+                                right = source;
+
+                                on = ReadWhere(arg, right, predicateLambda);
+                                return;
+                            }
+                        case "DefaultIfEmpty":
+                            {
+                                joinType = "leftJoin";
+                                var source = call.arguments[0];
+                                ReadNode(arg, source);
+                                return;
+                            }
+                    }
+                    throw new Exception("[CollectionStream] unexpected method call : " + call.methodName);
+                }
+
+            }
+            #endregion
+        }
+
+
+        // predicateLambda:          father => (father.id == user.fatherId)
+        ExpressionNode ReadWhere(Argument arg, IStream source, ExpressionNode_Lambda predicateLambda)
+        {
+            arg = arg.WithAlias(source, predicateLambda.parameterNames[0]);
+            ExpressionNode predicate = predicateLambda.body;
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) =>
+            {
+                if (node?.nodeType == NodeType.Member)
+                {
+                    ExpressionNode_Member member = node;
+
+                    if (!string.IsNullOrWhiteSpace(member.parameterName) && arg.streamAliasMap?.TryGetValue(member.parameterName, out var parameterValue) == true)
+                    {
+                        if (parameterValue is JoinedStream stream && stream.select?.TryGetField(node.memberName, out var sourceStream) == true)
+                        {
+                            return (true, (ExpressionNode)sourceStream);
+                        }
+                        return (true, ExpressionNode_RenameableMember.Member(parameterValue, node.memberName, node));
+                    }
+                }
+                return default;
+            };
+
+            return cloner.Clone(predicate);
+        }
+
+
+        SelectedFields ReadFieldSelect(ExpressionNode_Lambda resultSelector, params IStream[] args)
+        {
+            ExpressionNode node = resultSelector.body;
+            if (node?.nodeType != NodeType.New)
+                throw new NotSupportedException($"[CollectionStream] unexpected expression : {node.nodeType}");
+
+            #region #1 get args
+            var parameters = new Dictionary<string, IStream>();
+            for (var i = 0; i < args.Length; i++)
+            {
+                parameters[resultSelector.parameterNames[i]] = args[i];
+            }
+            #endregion
+
+            // #2 cloner
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) =>
+            {
+                if (node?.nodeType == NodeType.Member)
+                {
+                    ExpressionNode_Member member = node;
+
+                    // {"nodeType":"Member", "parameterName":"a0", "memberName":"id"}
+                    if (!string.IsNullOrWhiteSpace(member.parameterName) && parameters.TryGetValue(member.parameterName, out var parameterValue))
+                    {
+                        if (parameterValue is JoinedStream stream && stream.select?.TryGetField(node.memberName, out var sourceStream) == true)
+                        {
+                            return (true, sourceStream);
+                        }
+                        return (true, ExpressionNode_RenameableMember.Member(parameterValue, node.memberName, node));
+                    }
+
+                    // reduce level:  {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
+                    if (member.objectValue?.nodeType == NodeType.Member && member.objectValue.memberName == null)
+                    {
+                        var objectValue = cloner.Clone(member.objectValue);
+
+                        objectValue.memberName = member.memberName;
+                        objectValue.Member_SetType(member.Member_GetType());
+                        return (true, objectValue);
+                    }
+                }
+                return default;
+            };
+
+
+            // #3
+            bool? existCalculatedField = null;
+
+            // root value of ExpressionNode_Member is IStream
+
+            var fields = cloner.Clone(node) as ExpressionNode_New;
+
+            if (existCalculatedField != true)
+                existCalculatedField = fields.constructorArgs?.Exists(m => m?.value?.nodeType != NodeType.Member && m?.value?.nodeType != NodeType.New);
+
+            if (existCalculatedField != true)
+                existCalculatedField = fields.memberArgs?.Exists(m => m?.value?.nodeType != NodeType.Member && m?.value?.nodeType != NodeType.New);
+
+            return new() { fields = fields, existCalculatedField = existCalculatedField };
+        }
+
+
+
+        ExpressionNode ReadSortField(ExpressionNode_Lambda resultSelector, params IStream[] args)
+        {
+            ExpressionNode node = resultSelector.body;
+            if (node?.nodeType != NodeType.Member)
+                throw new NotSupportedException($"[CollectionStream] unexpected expression : {node.nodeType}");
+
+            #region #1 get args
+            var parameters = new Dictionary<string, IStream>();
+            for (var i = 0; i < args.Length; i++)
+            {
+                parameters[resultSelector.parameterNames[i]] = args[i];
+            }
+            #endregion
+
+            // #2 cloner
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) =>
+            {
+                if (node?.nodeType == NodeType.Member)
+                {
+                    ExpressionNode_Member member = node;
+
+                    // {"nodeType":"Member", "parameterName":"a0", "memberName":"id"}
+                    if (!string.IsNullOrWhiteSpace(member.parameterName) && parameters.TryGetValue(member.parameterName, out var parameterValue))
+                    {
+                        if (parameterValue is JoinedStream stream && stream.select?.TryGetField(node.memberName, out var sourceStream) == true)
+                        {
+                            return (true, sourceStream);
+                        }
+                        return (true, ExpressionNode_RenameableMember.Member(parameterValue, node.memberName, node));
+                    }
+
+                    // reduce level:  {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
+                    if (member.objectValue?.nodeType == NodeType.Member && member.objectValue.memberName == null)
+                    {
+                        var objectValue = cloner.Clone(member.objectValue);
+
+                        objectValue.memberName = member.memberName;
+                        objectValue.Member_SetType(member.Member_GetType());
+                        return (true, objectValue);
+                    }
+                }
+                return default;
+            };
+
+
+            // #3
+            var member = cloner.Clone(node);
+            return member;
+        }
+
+    }
+}

+ 156 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/CollectionsQuery/Streams.cs

@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+
+
+namespace Vit.Linq.ExpressionTree.ComponentModel.CollectionsQuery
+{
+    public interface IStream
+    {
+        string alias { get; }
+    }
+
+    public class SourceStream : IStream
+    {
+        public SourceStream(object source, string alias)
+        {
+            this.source = source;
+            this.alias = alias;
+        }
+        public string alias { get; private set; }
+        private object source;
+
+        public int? hashCode
+        {
+            get => source?.GetHashCode();
+        }
+
+        public object GetSource() => source;
+    }
+
+
+    public class StreamToJoin
+    {
+        // leftJoin , innerJoin
+        public string joinType { get; set; }
+        public IStream right { get; set; }
+
+        //  a1.id==b2.id
+        public ExpressionNode on { get; set; }
+    }
+
+
+    /* //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;
+     */
+
+    /* //linq
+value(Vit.Linq.Converter.OrderedQueryable`1[Vit.Linq.MsTest.Converter.Join_Test+User])
+.SelectMany(
+     user => value(Vit.Linq.MsTest.Converter.Join_Test+<>c__DisplayClass0_1).users
+             .Where(father => (Convert(father.id, Nullable`1) == user.fatherId)).DefaultIfEmpty(),
+     (user, father) => new <>f__AnonymousType4`2(user = user, father = father)
+ ).SelectMany(
+     item => value(Vit.Linq.MsTest.Converter.Join_Test+<>c__DisplayClass0_1).users
+                 .Where(mother => (Convert(mother.id, Nullable`1) == item.user.fatherId)).DefaultIfEmpty(),
+     (item, mother) => new <>f__AnonymousType5`3(user = item.user, father = item.father, mother = mother)
+ )
+.Skip().Take().Select()
+     */
+
+    public class SelectedFields
+    {
+        // root value of ExpressionNode_Member is IStream
+        public ExpressionNode_New fields;
+
+        public bool? existCalculatedField { get; set; }
+        internal bool TryGetField(string fieldName, out ExpressionNode field)
+        {
+            field = null;
+
+            var fieldInfo = fields?.memberArgs?.FirstOrDefault(m => m.name == fieldName);
+
+            fieldInfo ??= fields?.constructorArgs?.FirstOrDefault(m => m.name == fieldName);
+
+            if (fieldInfo != null)
+            {
+                field = fieldInfo.value;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public partial class JoinedStream : IStream
+    {
+
+        public JoinedStream(string alias)
+        {
+            this.alias = alias;
+        }
+
+        /// <summary>
+        /// default is ToList, could be :  Count | First | FirstOrDefault | Last | LastOrDefault | TotalCount
+        /// </summary>
+        public string method { get; set; }
+
+
+        public string alias { get; protected set; }
+
+        // ExpressionNode_New   new { c=a,d=b }
+        public SelectedFields select;
+        public bool? distinct;
+
+
+        public IStream left;
+
+
+        public List<StreamToJoin> joins;
+
+        //  a1.id==b2.id
+        public ExpressionNode where { get; set; }
+
+        //  a1.id, b2.id
+        public List<SortField> orders { get; set; }
+
+
+        public int? skip { get; set; }
+        public int? take { get; set; }
+
+    }
+
+
+    public partial class StreamToUpdate : JoinedStream
+    {
+        public StreamToUpdate(IStream source) : base(source.alias)
+        {
+            if (source is JoinedStream joinedStream)
+            {
+                this.select = joinedStream.select;
+                this.distinct = joinedStream.distinct;
+                this.left = joinedStream.left;
+                this.joins = joinedStream.joins;
+                this.where = joinedStream.where;
+                this.orders = joinedStream.orders;
+                this.skip = joinedStream.skip;
+                this.take = joinedStream.take;
+            }
+            else
+            {
+                left = source;
+            }
+        }
+
+        // ExpressionNode_New   new { name = name + "_" }
+        public ExpressionNode_New fieldsToUpdate;
+
+    }
+}

+ 0 - 43
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode.Binary.cs

@@ -1,43 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Vit.Linq.ExpressionTree.ComponentModel
-{
-    public interface ExpressionNode_Binary : IExpressionNode
-    {
-        public ExpressionNode left { get; set; }
-        public ExpressionNode right { get; set; }
-    }
-
-    public interface ExpressionNode_Equal : ExpressionNode_Binary { }
-    public interface ExpressionNode_NotEqual : ExpressionNode_Binary { }
-    public interface ExpressionNode_LessThan : ExpressionNode_Binary { }
-    public interface ExpressionNode_LessThanOrEqual : ExpressionNode_Binary { }
-    public interface ExpressionNode_GreaterThan : ExpressionNode_Binary { }
-    public interface ExpressionNode_GreaterThanOrEqual : ExpressionNode_Binary { }
-
-
-    public partial class ExpressionNode :
-        ExpressionNode_Equal, ExpressionNode_NotEqual,
-        ExpressionNode_LessThan, ExpressionNode_LessThanOrEqual,
-        ExpressionNode_GreaterThan, ExpressionNode_GreaterThanOrEqual
-    {
-        //public ExpressionNode left { get; set; }
-        //public ExpressionNode right { get; set; }
-
-
-        public static ExpressionNode Equal(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.Equal, left = left, right = right };
-        public static ExpressionNode NotEqual(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.NotEqual, left = left, right = right };
-        public static ExpressionNode LessThan(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.LessThan, left = left, right = right };
-        public static ExpressionNode LessThanOrEqual(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.LessThanOrEqual, left = left, right = right };
-        public static ExpressionNode GreaterThan(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.GreaterThan, left = left, right = right };
-        public static ExpressionNode GreaterThanOrEqual(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.GreaterThanOrEqual, left = left, right = right };
-    }
-}

+ 0 - 15
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode.cs

@@ -1,15 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Vit.Linq.ExpressionTree.ComponentModel
-{
-    public interface IExpressionNode
-    {
-        string nodeType { get; }
-    }
-    public partial class ExpressionNode : IExpressionNode
-    {
-        public string nodeType { get; set; }
-    }
-}

+ 13 - 8
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode.AndOr.cs → src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode.AndOr.cs

@@ -2,30 +2,35 @@
 using System.Collections.Generic;
 using System.Text;
 
+using Vit.Linq.Filter;
+ 
+
+
 namespace Vit.Linq.ExpressionTree.ComponentModel
 {
+
     public interface ExpressionNode_And : IExpressionNode
     {
-        public ExpressionNode left { get; set; }
-        public ExpressionNode right { get; set; }
+        public ExpressionNode left { get;  }
+        public ExpressionNode right { get;   }
     }
+
     public interface ExpressionNode_Or : IExpressionNode
     {
-        public ExpressionNode left { get; set; }
-        public ExpressionNode right { get; set; }
+        public ExpressionNode left { get; }
+        public ExpressionNode right { get; }
     }
 
-
     public partial class ExpressionNode : ExpressionNode_And, ExpressionNode_Or
     {
         public ExpressionNode left { get; set; }
         public ExpressionNode right { get; set; }
 
-
         public static ExpressionNode And(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.And, left = left, right = right };
+             => new ExpressionNode { nodeType = NodeType.And, expressionType = "Binary", left = left, right = right };
 
         public static ExpressionNode Or(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.Or, left = left, right = right };
+             => new ExpressionNode { nodeType = NodeType.Or, expressionType = "Binary", left = left, right = right };
+
     }
 }

+ 41 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode.Binary.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Vit.Linq.Filter;
+
+
+
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+
+    public interface ExpressionNode_Binary : IExpressionNode
+    {
+        public ExpressionNode left { get; }
+        public ExpressionNode right { get; }
+    }
+
+    public interface ExpressionNode_Equal : ExpressionNode_Binary { }
+    public interface ExpressionNode_NotEqual : ExpressionNode_Binary { }
+    public interface ExpressionNode_LessThan : ExpressionNode_Binary { }
+    public interface ExpressionNode_LessThanOrEqual : ExpressionNode_Binary { }
+    public interface ExpressionNode_GreaterThan : ExpressionNode_Binary { }
+    public interface ExpressionNode_GreaterThanOrEqual : ExpressionNode_Binary { }
+
+
+    public partial class ExpressionNode :
+        ExpressionNode_Equal, ExpressionNode_NotEqual,
+        ExpressionNode_LessThan, ExpressionNode_LessThanOrEqual,
+        ExpressionNode_GreaterThan, ExpressionNode_GreaterThanOrEqual
+    {
+        //public ExpressionNode left { get; set; }
+        //public ExpressionNode right { get; set; }
+
+
+        public static ExpressionNode Binary(string nodeType, ExpressionNode left, ExpressionNode right)
+            => new ExpressionNode { nodeType = nodeType, expressionType = "Binary", left = left, right = right };
+
+    }
+}

+ 37 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode.cs

@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+    public interface IExpressionNode
+    {
+        string nodeType { get; }
+
+        /// <summary>
+        /// could be: Unary , Binary
+        /// </summary>
+        string expressionType { get; }
+
+        object GetCodeArg(string key);
+        void SetCodeArg(string key, object arg);
+    }
+
+    public partial class ExpressionNode : IExpressionNode
+    {
+        public string nodeType { get; set; }
+
+
+        /// <summary>
+        /// could be: Unary , Binary
+        /// </summary>
+        public string expressionType { get; set; }
+
+        protected Dictionary<string, object> codeArg;
+        public object GetCodeArg(string key) => codeArg?.TryGetValue(key, out var value) == true ? value : null;
+        public Dictionary<string, object> GetCodeArg() => codeArg;
+        public void SetCodeArg(string key, object arg)
+        {
+            codeArg ??= new();
+            codeArg[key] = arg;
+        }
+    }
+}

+ 58 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNodeCloner.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+
+namespace Vit.Linq.ExpressionTree.ComponentModel 
+{
+    public class ExpressionNodeCloner
+    {
+        public virtual Func<ExpressionNode, (bool success, ExpressionNode dest)> clone { get; set; }
+        public virtual ExpressionNode Clone(ExpressionNode node)
+        {
+            if (clone != null)
+            {
+                var result = clone(node);
+                if (result.success) return result.dest;
+            }
+            return CloneChildren(node, new ExpressionNode());
+        }
+
+        public virtual ExpressionNode CloneChildren(ExpressionNode source, ExpressionNode dest)
+        {
+            foreach (var p in typeof(ExpressionNode).GetProperties())
+            {
+                if (p.CanRead && p.CanWrite)
+                {
+                    var value = p.GetValue(source);
+                    if (value == null) continue;
+                    if (value is ExpressionNode child)
+                    {
+                        value = Clone(child);
+                    }
+                    else if (value is ExpressionNode[] childrenArray)
+                    {
+                        value = childrenArray.Select(child => Clone(child)).ToArray();
+                    }
+                    else if (value is List<ExpressionNode> childrenList)
+                    {
+                        value = childrenList.Select(child => Clone(child)).ToList();
+                    }
+                    else if (value is List<MemberBind> members)
+                    {
+                        value = members.Select(member => new MemberBind { name = member.name, value = Clone(member.value) }).ToList();
+                    }
+                    p.SetValue(dest, value);
+                }
+            }
+            var codeArg = source.GetCodeArg();
+            if (codeArg != null)
+            {
+                foreach (var kv in codeArg)
+                    dest.SetCodeArg(kv.Key, kv.Value);
+            }
+            return dest;
+        }
+    }
+}

+ 8 - 3
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_ArrayIndex.cs → src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_ArrayIndex.cs

@@ -2,10 +2,15 @@
 using System.Collections.Generic;
 using System.Text;
 
+using Vit.Linq.Filter;
+
+
+
 namespace Vit.Linq.ExpressionTree.ComponentModel
 {
+
     /// <summary>
-    /// left:   object
+    /// left:  object
     /// right:  arguments[0]
     /// </summary>
     public interface ExpressionNode_ArrayIndex : ExpressionNode_Binary
@@ -14,13 +19,13 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
         //public ExpressionNode right { get; }
     }
 
-
     public partial class ExpressionNode : ExpressionNode_ArrayIndex
     {
         //public ExpressionNode left { get; set; }
         //public ExpressionNode right { get; set; }
 
+
         public static ExpressionNode ArrayIndex(ExpressionNode left = null, ExpressionNode right = null)
-            => new ExpressionNode { nodeType = NodeType.ArrayIndex, left = left, right = right };
+            => new ExpressionNode { nodeType = NodeType.ArrayIndex, expressionType = "Binary", left = left, right = right };
     }
 }

+ 11 - 19
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Constant.cs → src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Constant.cs

@@ -1,28 +1,27 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
-using System.Linq.Expressions;
-using System.Text;
 
 using Vit.Extensions.Linq_Extensions;
 
+
 namespace Vit.Linq.ExpressionTree.ComponentModel
 {
 
     public interface ExpressionNode_Constant : IExpressionNode
     {
-        public object value { get; set; }
         public ValueType valueType { get; set; }
 
-        public Expression ConstantToExpression(ExpressionConvertService service);
-    }
+        public object value { get; set; }
 
+    }
 
     public partial class ExpressionNode : ExpressionNode_Constant
     {
-        public object value { get; set; }
+
         public ValueType valueType { get; set; }
 
+        public object value { get; set; }
+
 
         public static ExpressionNode Constant(object value = null, Type type = null)
         {
@@ -30,7 +29,9 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
             {
                 value = query.IQueryable_ToList();
             }
+
             var valueType = ValueType.FromType(type);
+
             return new ExpressionNode
             {
                 nodeType = NodeType.Constant,
@@ -39,17 +40,8 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
             };
         }
 
-        public Expression ConstantToExpression(ExpressionConvertService service)
-        {
-            var value = this.value;
-            Type type = this.valueType?.ToType();
-            if (type == null) return Expression.Constant(value);
-
-            if (value != null && !type.IsAssignableFrom(value.GetType()))
-            {
-                value = service.ConvertToType(value, type);
-            }
-            return Expression.Constant(value, type);
-        }
     }
+
+
+   
 }

+ 17 - 8
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Convert.cs → src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Convert.cs

@@ -1,29 +1,38 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
 using System.Text;
 
-using Vit.Extensions.Linq_Extensions;
+using Vit.Linq.Filter;
+
+ 
 
 namespace Vit.Linq.ExpressionTree.ComponentModel
 {
 
     public interface ExpressionNode_Convert : IExpressionNode
     {
-        public ValueType valueType { get; set; }
-        public ExpressionNode body { get; set; }
-    }
+        public ValueType valueType { get;  }
 
+        public ExpressionNode body { get;   }
+    }
 
     public partial class ExpressionNode : ExpressionNode_Convert
     {
+
         //public ValueType valueType { get; set; }
-        //public ExpressionNode body { get; set; }
 
+        //public ExpressionNode body { get; set; }
 
         public static ExpressionNode Convert(ValueType valueType = null, ExpressionNode body = null)
-            => new ExpressionNode { nodeType = NodeType.Convert, valueType = valueType, body = body };
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.Convert,
+                expressionType = "Unary",
+                valueType = valueType,
+                body = body,
+            };
+        }
 
     }
 }

+ 48 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Lambda.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+
+    public interface ExpressionNode_Lambda : IExpressionNode
+    {
+        public string[] parameterNames { get; }
+
+        public ExpressionNode body { get; }
+
+        Type[] Lambda_GetParamTypes();
+        ExpressionNode Lambda_SetParamTypes(Type[] paramTypes);
+    }
+
+    public partial class ExpressionNode : ExpressionNode_Lambda
+    {
+        public string[] parameterNames { get; set; }
+
+        public ExpressionNode body { get; set; }
+
+        public static ExpressionNode Lambda(string[] parameterNames, ExpressionNode body)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.Lambda,
+                parameterNames = parameterNames,
+                body = body,
+            };
+        }
+        public Type[] Lambda_GetParamTypes()
+        {
+            return GetCodeArg("Lambda_ParamTypes") as Type[];
+        }
+
+        public ExpressionNode Lambda_SetParamTypes(Type[] paramTypes)
+        {
+            SetCodeArg("Lambda_ParamTypes", paramTypes);
+            return this;
+        }
+
+
+    }
+}

+ 116 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Member.cs

@@ -0,0 +1,116 @@
+using System;
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+
+    public interface ExpressionNode_Member : IExpressionNode
+    {
+        public string parameterName { get;   }
+        public ExpressionNode objectValue { get;  }
+
+        public string memberName { get; }
+
+        ExpressionNode Member_SetType(Type type);
+        Type Member_GetType();
+    }
+
+    public partial class ExpressionNode : ExpressionNode_Member
+    {
+        public virtual string parameterName { get; set; }
+
+        public ExpressionNode objectValue { get; set; }
+
+        public string memberName { get; set; }
+
+
+        public static ExpressionNode Member(ExpressionNode objectValue, string memberName, string parameterName)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.Member,
+                objectValue = objectValue,
+                memberName = memberName,
+                parameterName = parameterName,
+            };
+        }
+
+        public static ExpressionNode Member(ExpressionNode objectValue, string memberName)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.Member,
+                objectValue = objectValue,
+                memberName = memberName,
+            };
+        }
+        public static ExpressionNode Member(string parameterName, string memberName)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.Member,
+                parameterName = parameterName,
+                memberName = memberName,
+            };
+        }
+
+        public ExpressionNode Member_SetType(Type type)
+        {
+            SetCodeArg("Member_Type", type);
+            return this;
+        }
+        public Type Member_GetType()
+        {
+            return GetCodeArg("Member_Type") as Type;
+        }
+    }
+
+    public class ParamterInfo
+    {
+        internal ParamterInfo(object value, Type type)
+        {
+            this.value = value;
+            this.type = type;
+        }
+
+        public object value { get; private set; }
+        public Type type { get; private set; }
+        //public int hashCode => value?.GetHashCode() ?? 0;
+        public String parameterName { get; private set; }
+
+        public void Rename(String parameterName) => this.parameterName = parameterName;
+    }
+    public class ExpressionNode_FreeParameter : ExpressionNode
+    {
+        protected ParamterInfo parameter;
+
+        public override string parameterName
+        {
+            set => throw new NotSupportedException();
+            get => parameter?.parameterName;
+        }
+
+        public static ExpressionNode Member(ParamterInfo parameter)
+        {
+            var node = new ExpressionNode_FreeParameter
+            {
+                nodeType = NodeType.Member,
+                parameter = parameter,
+            };
+            node.Member_SetOriValue(parameter?.value);
+            return node;
+        }
+    }
+
+    public static partial class ExpressionNode_Extensions
+    {
+        public static object Member_GetOriValue(this ExpressionNode_Member member)
+        {
+            return member.GetCodeArg("Member_OriValue");
+        }
+        public static void Member_SetOriValue(this ExpressionNode_Member member, object oriGalue)
+        {
+            member.SetCodeArg("Member_OriValue", oriGalue);
+        }
+    }
+
+}

+ 50 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_MethodCall.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Vit.Linq.Filter;
+
+ 
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+
+    public interface ExpressionNode_MethodCall : IExpressionNode
+    {
+        /// <summary>
+        ///     the System.Linq.Expressions.Expression that represents the instance for instance method calls or null for static method calls.
+        /// </summary>
+        public ExpressionNode @object { get;}
+
+        public string typeName { get; }
+        public string methodName { get;  }
+
+        public ExpressionNode[] arguments { get; }
+    }
+
+    public partial class ExpressionNode : ExpressionNode_MethodCall
+    {
+
+        /// <summary>
+        ///     the System.Linq.Expressions.Expression that represents the instance for instance method calls or null for static method calls.
+        /// </summary>
+        public ExpressionNode @object { get; set; }
+
+        public string typeName { get; set; }
+        public string methodName { get; set; }
+
+        public ExpressionNode[] arguments { get; set; }
+
+        public static ExpressionNode MethodCall(string typeName = null, string methodName = null, ExpressionNode @object = null, ExpressionNode[] arguments = null)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.MethodCall,
+                typeName = typeName,
+                methodName = methodName,
+                @object = @object,
+                arguments = arguments,
+            };
+        }
+    }
+}

+ 51 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_New.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+
+    public class MemberBind
+    {
+        public string name { get; set; }
+        public ExpressionNode value { get; set; }
+    }
+
+
+    public interface ExpressionNode_New : IExpressionNode
+    {
+        List<MemberBind> constructorArgs { get; set; }
+        List<MemberBind> memberArgs { get; set; }
+
+        Type New_GetType();
+        ExpressionNode New_SetType(Type type);
+    }
+
+    public partial class ExpressionNode : ExpressionNode_New
+    {
+        public List<MemberBind> constructorArgs { get; set; }
+        public List<MemberBind> memberArgs { get; set; }
+
+        public static ExpressionNode New(List<MemberBind> constructorArgs = null, List<MemberBind> memberArgs = null)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.New,
+                constructorArgs = constructorArgs,
+                memberArgs = memberArgs,
+            };
+        }
+
+
+        public Type New_GetType()
+        {
+            return GetCodeArg("New_Type") as Type;
+        }
+
+        public ExpressionNode New_SetType(Type type)
+        {
+            SetCodeArg("New_Type", type);
+            return this;
+        }
+    }
+
+}

+ 29 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/ExpressionNode_Not.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Vit.Linq.Filter;
+ 
+
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+    public interface ExpressionNode_Not : IExpressionNode
+    {
+        public ExpressionNode body { get; }
+    }
+
+    public partial class ExpressionNode : ExpressionNode_Not
+    {
+        //public ExpressionNode body { get; set; }
+
+        public static ExpressionNode Not(ExpressionNode body = null)
+        {
+            return new ExpressionNode
+            {
+                nodeType = NodeType.Not,
+                expressionType = "Unary",
+                body = body,
+            };
+        }
+    }
+}

+ 18 - 0
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode/SortField.cs

@@ -0,0 +1,18 @@
+namespace Vit.Linq.ExpressionTree.ComponentModel
+{
+
+    public class SortField
+    {
+        /// <summary>
+        ///
+        /// </summary>
+        public ExpressionNode member;
+
+        /// <summary>
+        ///  Gets or sets a value indicating whether the order is ascending (true) or descending (false).
+        /// </summary>
+        public bool asc;
+    }
+
+
+}

+ 0 - 47
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Call.cs

@@ -1,47 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Vit.Linq.ExpressionTree.ComponentModel
-{
-    /// <summary>
-    /// left:   object
-    /// right:  arguments[0]
-    /// </summary>
-    public interface ExpressionNode_Call : IExpressionNode
-    {
-        /// <summary>
-        ///   the System.Linq.Expressions.Expression that represents the instance for instance method calls or null for static method calls.
-        /// </summary>
-        public ExpressionNode instance { get; set; }
-
-        public string methodName { get; set; }
-
-        public ExpressionNode[] methodArguments { get; set; }
-    }
-
-
-    public partial class ExpressionNode : ExpressionNode_Call
-    {
-
-        /// <summary>
-        ///   the System.Linq.Expressions.Expression that represents the instance for instance method calls or null for static method calls.
-        /// </summary>
-        public ExpressionNode instance { get; set; }
-
-        public string methodName { get; set; }
-
-        public ExpressionNode[] methodArguments { get; set; }
-
-        public static ExpressionNode Call(ExpressionNode instance = null, string methodName = null, ExpressionNode[] methodArguments = null)
-        {
-            return new ExpressionNode
-            {
-                nodeType = NodeType.Call,
-                instance = instance,
-                methodName = methodName,
-                methodArguments = methodArguments
-            };
-        }
-    }
-}

+ 0 - 21
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Lambda.cs

@@ -1,21 +0,0 @@
-namespace Vit.Linq.ExpressionTree.ComponentModel
-{
-
-    public interface ExpressionNode_Lambda : IExpressionNode
-    {
-        public string[] parameterNames { get; set; }
-        public ExpressionNode body { get; set; }
-    }
-
-
-    public partial class ExpressionNode : ExpressionNode_Lambda
-    {
-        public string[] parameterNames { get; set; }
-        public ExpressionNode body { get; set; }
-
-
-        public static ExpressionNode Lambda(string[] parameterNames = null, ExpressionNode body = null)
-            => new ExpressionNode { nodeType = NodeType.Lambda, parameterNames = parameterNames, body = body };
-
-    }
-}

+ 0 - 21
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Member.cs

@@ -1,21 +0,0 @@
-namespace Vit.Linq.ExpressionTree.ComponentModel
-{
-
-    public interface ExpressionNode_Member : IExpressionNode
-    {
-        public string memberName { get; set; }
-        public string parameterName { get; set; }
-        public ExpressionNode objectValue { get; set; }
-    }
-
-
-    public partial class ExpressionNode : ExpressionNode_Member
-    {
-        public string memberName { get; set; }
-        public string parameterName { get; set; }
-        public ExpressionNode objectValue { get; set; }
-
-        public static ExpressionNode Member(string memberName = null, string parameterName = null, ExpressionNode objectValue = null)
-            => new ExpressionNode { nodeType = NodeType.Member, memberName = memberName, parameterName = parameterName, objectValue = objectValue };
-    }
-}

+ 0 - 17
src/Vit.Linq/ExpressionTree/ComponentModel/ExpressionNode_Not.cs

@@ -1,17 +0,0 @@
-namespace Vit.Linq.ExpressionTree.ComponentModel
-{
-
-    public interface ExpressionNode_Not : IExpressionNode
-    {
-        public ExpressionNode body { get; set; }
-    }
-
-
-    public partial class ExpressionNode : ExpressionNode_Not
-    {
-        //public ExpressionNode body { get; set; }
-
-        public static ExpressionNode Not(ExpressionNode body = null)
-            => new ExpressionNode { nodeType = NodeType.Not, body = body };
-    }
-}

+ 18 - 17
src/Vit.Linq/ExpressionTree/ComponentModel/NodeType.cs

@@ -4,29 +4,30 @@ using System.Text;
 
 namespace Vit.Linq.ExpressionTree.ComponentModel
 {
-    public class NodeType
+    public static class NodeType
     {
-        public const string Member=nameof(Member);
-        public const string Constant = nameof(Constant);
-        public const string Convert = nameof(Convert);
+        public const string Member = "Member";
+        public const string Constant = "Constant";
+        public const string Convert = "Convert";
 
-        public const string And = nameof(And);
-        public const string Or = nameof(Or);
-        public const string Not = nameof(Not);
+        public const string And = "And";
+        public const string Or = "Or";
+        public const string Not = "Not";
 
+        public const string Equal = "Equal";
+        public const string NotEqual = "NotEqual";
+        public const string LessThan = "LessThan";
+        public const string LessThanOrEqual = "LessThanOrEqual";
+        public const string GreaterThan = "GreaterThan";
+        public const string GreaterThanOrEqual = "GreaterThanOrEqual";
 
-        public const string Equal = nameof(Equal);
-        public const string NotEqual = nameof(NotEqual);
-        public const string LessThan = nameof(LessThan);
-        public const string LessThanOrEqual = nameof(LessThanOrEqual);
-        public const string GreaterThan = nameof(GreaterThan);
-        public const string GreaterThanOrEqual = nameof(GreaterThanOrEqual);
+        public const string Lambda = "Lambda";
 
+        public const string MethodCall = nameof(MethodCall);
+        public const string ArrayIndex = "ArrayIndex";
 
-        public const string Lambda = nameof(Lambda);
+        public const string New = nameof(New);
 
-
-        public const string Call = nameof(Call);
-        public const string ArrayIndex = nameof(ArrayIndex);
     }
+
 }

+ 144 - 14
src/Vit.Linq/ExpressionTree/ComponentModel/ValueType.cs

@@ -2,23 +2,24 @@
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 
 namespace Vit.Linq.ExpressionTree.ComponentModel
 {
+
     public partial class ValueType
     {
         /// <summary>
-        /// valueType:      String | Int32 | Int64 | Single | Double | Boolean | ...
-        /// Object:         Object
-        /// List:           List<String>
-        /// Array:          String[]
-        /// List:           List<String>
-        /// Nullable:       int?
+        ///   ValueType:      String | Int32 | Int64 | Single | Double | Boolean | DateTime | ...
+        ///   Nullable:       int?
+        ///   Object:         Object
+        ///   List:           List<String>
+        ///   Array:          String[]
+        ///   Queryable:      IQueryable<string>
+        ///   Enumerable:     IEnumerable<string>
+        ///   Collection:     ICollection<string>
         /// </summary>
         public string typeName { get; set; }
 
-
         public ValueType[] genericArgumentTypes { get; set; }
 
 
@@ -27,7 +28,19 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
         {
             if (type == null) return null;
             if (type == typeof(string))
-                return new ValueType { typeName = "String" };
+            {
+                return new ValueType
+                {
+                    typeName = "String"
+                };
+            }
+            if (type == typeof(object))
+            {
+                return new ValueType
+                {
+                    typeName = "Object"
+                };
+            }
 
             if (type.IsValueType)
             {
@@ -43,14 +56,21 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
                 }
 
                 var typeName = type.Name;
-                return new ValueType { typeName = typeName };
+                return new ValueType
+                {
+                    typeName = typeName
+                };
             }
 
             if (type.IsArray)
             {
                 var elementType = type.GetElementType();
                 var genericArgumentTypes = new[] { FromType(elementType) };
-                return new ValueType { typeName = "Array", genericArgumentTypes = genericArgumentTypes };
+                return new ValueType
+                {
+                    typeName = "Array",
+                    genericArgumentTypes = genericArgumentTypes
+                };
             }
 
             if (type.IsGenericType)
@@ -90,6 +110,116 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
         #endregion
 
 
+
+        #region ConvertToType
+        public static object ConvertToType(object oriValue, Type targetType)
+        {
+            if (oriValue != null && targetType.IsAssignableFrom(oriValue.GetType()))
+            {
+                return oriValue;
+            }
+
+            if (targetType.IsValueType || targetType == typeof(string))
+            {
+                // #1 Nullable
+                if (targetType.IsGenericType && typeof(Nullable<>) == targetType.GetGenericTypeDefinition())
+                {
+                    if (oriValue == null) return null;
+                    var elementType = targetType.GetGenericArguments()[0];
+                    return ConvertToPrimitiveType(oriValue, elementType);
+                }
+
+                // #2 valueType  and string and DateTime
+                return ConvertToPrimitiveType(oriValue, targetType);
+            }
+
+            if (oriValue == null) return null;
+
+            // #3 collection  include(Array,List,Queryable,Enumerable)
+            if (targetType.IsArray)
+            {
+                var elementType = targetType.GetElementType();
+                return ConvertToList(oriValue, targetType, elementType);
+            }
+
+            if (targetType.IsGenericType)
+            {
+                if (typeof(IList).IsAssignableFrom(targetType)
+                    || typeof(IQueryable).IsAssignableFrom(targetType)
+                    || typeof(IEnumerable).IsAssignableFrom(targetType)
+                    || typeof(ICollection).IsAssignableFrom(targetType)
+                )
+                {
+                    var elementType = targetType.GetGenericArguments()[0];
+                    return ConvertToList(oriValue, targetType, elementType);
+                }
+            }
+
+            // #4 other
+            throw new ArgumentException($"can not convert value({oriValue}) to type({targetType.Name}) from type({oriValue?.GetType().Name})");
+        }
+        static object ConvertToList(object items, Type collectionType, Type elemType)
+        {
+            return new Func<object, Type, object>(ConvertToList<string>)
+                   .Method.GetGenericMethodDefinition().MakeGenericMethod(elemType)
+               .Invoke(null, new[] { items, collectionType });
+        }
+        static object ConvertToList<T>(object items, Type collectionType)
+        {
+            var type = typeof(T);
+            List<T> list = new List<T>();
+            if (items is IEnumerable enumerable)
+            {
+                foreach (var item in enumerable)
+                {
+                    list.Add((T)ConvertToType(item, type));
+                }
+            }
+            if (collectionType.IsArray)
+                return list.ToArray();
+
+            if (typeof(IQueryable).IsAssignableFrom(collectionType))
+                return list.AsQueryable();
+
+            return list;
+        }
+
+        /// <summary>
+        /// targetType must be ValueType or string or DateTime
+        /// </summary>
+        /// <param name="oriValue"></param>
+        /// <param name="targetType"></param>
+        /// <returns></returns>
+        public static object ConvertToPrimitiveType(object oriValue, Type targetType)
+        {
+            if (oriValue == null)
+            {
+                if (targetType == typeof(string)) return null;
+                return Activator.CreateInstance(targetType);
+            }
+
+            if (oriValue.GetType() == targetType) return oriValue;
+
+            var typeName = targetType.Name.ToLower();
+
+            // double -> int         "12.1"  12.1  ->  12
+            if (typeName.Contains("int") || typeName.Contains("byte"))
+            {
+                oriValue = ((decimal)Convert.ChangeType(oriValue, typeof(decimal))).ToString("#");
+            }
+            // bool -> string       true/false  -> "true" / "false"
+            else if (targetType == typeof(string) && oriValue is bool b)
+            {
+                return b ? "true" : "false";
+            }
+
+            return Convert.ChangeType(oriValue, targetType);
+        }
+        #endregion
+
+
+
+
         #region ToType
         public Type ToType()
         {
@@ -129,13 +259,13 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
                     }
                 case "Object":
                     {
-                        return null;
+                        return typeof(object);
                     }
                 default: return Type.GetType("System." + typeName);
             }
         }
-        #endregion
-
 
+        #endregion
     }
+
 }

+ 97 - 0
src/Vit.Linq/ExpressionTree/DataConvertArgument.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree
+{
+
+    public class DataConvertArgument
+    {
+        public bool autoReduce { get; set; } = false;
+
+
+        public bool ReduceValue<T>(Expression expression, out T value)
+        {
+            try
+            {
+                if (autoReduce)
+                {
+                    var del = Expression.Lambda(expression).Compile();
+                    value = (T)del.DynamicInvoke();
+                    return true;
+                }
+            }
+            catch (Exception ex)
+            {
+            }
+            value = default;
+            return false;
+        }
+
+        public ExpressionConvertService convertService { get; set; }
+
+        private readonly List<string> usedParameterNames = new List<string>();
+
+        public void RegisterParameterNames(IEnumerable<string> names)
+        {
+            usedParameterNames.AddRange(names);
+        }
+
+        public void GenerateGlobalParameterName()
+        {
+            #region GetUnusedParameterName
+            int i = 0;
+            string GetUnusedParameterName()
+            {
+                for (; ; i++)
+                {
+                    var parameterName = "Param_" + i;
+                    if (!usedParameterNames.Contains(parameterName))
+                    {
+                        usedParameterNames.Add(parameterName);
+                        return parameterName;
+                    }
+                }
+            }
+            #endregion
+
+            globalParameters?.ForEach(p =>
+            {
+                if (string.IsNullOrWhiteSpace(p.parameterName))
+                {
+                    p.Rename(GetUnusedParameterName());
+                }
+            });
+
+        }
+
+        internal List<ParamterInfo> globalParameters { get; private set; }
+
+
+        public ExpressionNode GetParameter(object value, Type type)
+        {
+            ParamterInfo parameter;
+
+            parameter = globalParameters?.FirstOrDefault(p => p.value?.GetHashCode() == value.GetHashCode());
+
+            if (parameter == null)
+            {
+                if (globalParameters == null) globalParameters = new List<ParamterInfo>();
+
+                parameter = new ParamterInfo(value: value, type: type);
+                globalParameters.Add(parameter);
+            }
+            return ExpressionNode_FreeParameter.Member(parameter);
+        }
+
+
+
+      
+    }
+
+
+
+}

+ 0 - 32
src/Vit.Linq/ExpressionTree/ExpressionConvertService.MethodCall.cs

@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
-using System.Text;
-
-using Vit.Linq.ExpressionTree.ComponentModel;
-
-namespace Vit.Linq.ExpressionTree
-{
-    public partial class ExpressionConvertService
-    {
-        public interface IMethodConvertor
-        {
-            int priority { get; }
-        }
-
-
-        #region Data -> Code
-        protected virtual Expression ConvertMethodToExpression(CodeConvertArgument arg, ExpressionNode_Call call)
-        {
-            throw new NotSupportedException($"Unsupported  ExpressionNode call : {call.methodName}");
-        }
-        #endregion
-
-        #region Code -> Data
-        ExpressionNode ConvertExpression(DataConvertArgument arg, MethodCallExpression call)
-        {
-            throw new NotSupportedException($"Unsupported method call : {call.Method.Name}");
-        }
-        #endregion
-    }
-}

+ 0 - 18
src/Vit.Linq/ExpressionTree/ExpressionConvertService.Serialize.cs

@@ -1,18 +0,0 @@
- using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Vit.Linq.ExpressionTree
-{
-    public partial class ExpressionConvertService
-    {
-
-        public Func<object, Type, Object> convertToType { get; set; }
-
-        public object ConvertToType(Object value, Type valueType)
-        {
-            return convertToType.Invoke(value, valueType);
-        }
-
-    }
-}

+ 42 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToCode.Extensions.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree
+{
+
+    public partial class ExpressionConvertService
+    {
+        public Func<T, bool> ToPredicate<T>(ExpressionNode data)
+        {
+            return ToPredicateExpression<T>(data)?.Compile();
+        }
+
+        public string ToExpressionString<T>(ExpressionNode data)
+        {
+            return ToPredicateExpression<T>(data)?.ToString();
+        }
+
+
+        public Expression<Func<T, bool>> ToPredicateExpression<T>(ExpressionNode data)
+        {
+            var exp = ToLambdaExpression(data, typeof(T));
+            return (Expression<Func<T, bool>>)exp;
+        }
+
+        public LambdaExpression ToLambdaExpression(ExpressionNode_Lambda lambda, params Type[] paramTypes)
+        {
+            var arg = CodeConvertArgument.WithParams(this);
+            return ToLambdaExpression(arg, lambda, paramTypes);
+        }
+
+        public LambdaExpression ToLambdaExpression(ExpressionNode_Lambda lambda, Type[] paramTypes, Type[] newNodeResultTypes)
+        {
+            int i = 0;
+            var arg = CodeConvertArgument.WithParams(this, getResultTypeForNewNode: (newNode) => newNodeResultTypes[i++]);
+            return ToLambdaExpression(arg, lambda, paramTypes);
+        }
+
+    }
+}

+ 11 - 135
src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToCode.cs

@@ -1,159 +1,35 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Linq.Expressions;
-using System.Text;
 
 using Vit.Linq.ExpressionTree.ComponentModel;
 
 namespace Vit.Linq.ExpressionTree
 {
-    public class CodeConvertArgument
-    {
-        public List<ParameterExpression> parameters;
-
-        public CodeConvertArgument WithParams(params ParameterExpression[] newParams)
-        {
-            CodeConvertArgument source = this;
-
-            var arg = new CodeConvertArgument();
-            if (source?.parameters?.Any() == true)
-            {
-                arg.parameters = source.parameters.ToList();
-            }
-            else
-            {
-                arg.parameters = new List<ParameterExpression>();
-            }
-            if (newParams.Any())
-            {
-                arg.parameters.AddRange(newParams);
-            }
-            return arg;
-        }
-    }
-
     public partial class ExpressionConvertService
     {
-
-        public Func<T, bool> ToPredicate<T>(ExpressionNode data)
-        {
-            return ToPredicateExpression<T>(data)?.Compile();
-        }
-
-        public string ToExpressionString<T>(ExpressionNode data)
-        {
-            return ToPredicateExpression<T>(data)?.ToString();
-        }
-        public Expression<Func<T, bool>> ToPredicateExpression<T>(ExpressionNode data, string paramName = null)
-        {
-            var exp = ToLambdaExpression(data, typeof(T), paramName);
-            return (Expression<Func<T, bool>>)exp;
-        }
-
-        public LambdaExpression ToLambdaExpression(ExpressionNode data, Type paramType, string paramName = null)
-        {
-            ExpressionNode_Lambda lambda;
-
-            if (data.nodeType == NodeType.Lambda)
-            {
-                lambda = data;
-            }
-            else
-            {
-                lambda = ExpressionNode.Lambda(parameterNames: new string[] { paramName }, body: data);
-            }
-            return ToLambdaExpression(null, lambda, paramType);
-        }
-
-        public LambdaExpression ToLambdaExpression(CodeConvertArgument arg, ExpressionNode_Lambda lambda, params Type[] paramTypes)
+        public virtual LambdaExpression ToLambdaExpression(CodeConvertArgument arg, ExpressionNode_Lambda lambda, params Type[] paramTypes)
         {
-            string[] parameterNames = lambda.parameterNames ?? new string[0];
-
-            var parameters = parameterNames.Select((name, i) =>
-            {
-                var type = paramTypes.Length > i ? paramTypes[i] : typeof(object);
-
-                if (string.IsNullOrWhiteSpace(name))
-                    return Expression.Parameter(type);
-                return Expression.Parameter(type, name);
-            }).ToList();
-
-            arg = arg.WithParams(parameters.ToArray());
-
-            var expression = ToExpression(arg, lambda.body);
-            if (expression == null) return null;
-
-            return Expression.Lambda(expression, parameters);
+            lambda.Lambda_SetParamTypes(paramTypes);
+            return ToExpression(arg, lambda as ExpressionNode) as LambdaExpression;
         }
 
         public virtual Expression ToExpression(CodeConvertArgument arg, ExpressionNode data)
         {
             if (data == null) return null;
 
-            switch (data.nodeType)
+            foreach (var expressionConvertor in expressionConvertors)
             {
-                case NodeType.Member:
-                    ExpressionNode_Member member = data;
-                    ParameterExpression paramExp = null;
-                    if (member.parameterName != null) paramExp = arg.parameters.FirstOrDefault(p => p.Name == member.parameterName);
-                    if (paramExp == null) paramExp = arg.parameters.First();
-
-                    if (member.objectValue == null)
-                    {
-                        return LinqHelp.GetFieldMemberExpression(paramExp, member.memberName);
-                    }
-                    else
-                    {
-                        var instanceExp = ToExpression(arg, member.objectValue);
-                        return LinqHelp.GetFieldMemberExpression(instanceExp, member.memberName);
-                    }
-                case NodeType.Constant:
-                    ExpressionNode_Constant constant = data;
-                    return constant.ConstantToExpression(this);
-
-                case NodeType.Convert:
-                    {
-                        ExpressionNode_Convert convert = data;
-                        var value = ToExpression(arg, convert.body);
-
-                        Type type = convert.valueType?.ToType();
-                        if (type == null) type = value?.Type;
-
-                        return Expression.Convert(value, type);
-                    }
-
-                case NodeType.And:
-                    ExpressionNode_And and = data;
-                    return Expression.AndAlso(ToExpression(arg, and.left), ToExpression(arg, and.right));
-
-                case NodeType.Or:
-                    ExpressionNode_Or or = data;
-                    return Expression.OrElse(ToExpression(arg, or.left), ToExpression(arg, or.right));
-
-                case NodeType.Not:
-                    ExpressionNode_Not not = data;
-                    return Expression.Not(ToExpression(arg, not.body));
-
-                case NodeType.ArrayIndex:
-                    ExpressionNode_ArrayIndex arrayIndex = data;
-                    return Expression.ArrayIndex(ToExpression(arg, arrayIndex.left), ToExpression(arg, arrayIndex.right));
-
-                case NodeType.Equal: return Expression.Equal(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
-
-                case NodeType.NotEqual: return Expression.NotEqual(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
-                case NodeType.LessThan: return Expression.LessThan(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
-                case NodeType.LessThanOrEqual: return Expression.LessThanOrEqual(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
-                case NodeType.GreaterThan: return Expression.GreaterThan(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
-                case NodeType.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
-
-                case NodeType.Call:
-                    ExpressionNode_Call call = data;
-                    return ConvertMethodToExpression(arg, call);
+                var exp = expressionConvertor.ConvertToCode(arg, data);
+                if (exp != null) return exp;
             }
+
             throw new NotSupportedException(data.nodeType);
         }
 
 
+      
+
+     
+
     }
 }

+ 15 - 212
src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToData.cs

@@ -1,10 +1,6 @@
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
+using System;
 using System.Linq;
 using System.Linq.Expressions;
-using System.Text;
 
 using Vit.Linq.ExpressionTree.ComponentModel;
 
@@ -12,228 +8,35 @@ namespace Vit.Linq.ExpressionTree
 {
     public partial class ExpressionConvertService
     {
-        public class DataConvertArgument
+        public ExpressionNode ConvertToData(Expression expression, bool autoReduce = false)
         {
-            public ExpressionConvertService convertService { get; set; }
-            public List<ParamterInfo> parameters { get; set; }
-
-            public class ParamterInfo
-            {
-                public object value { get; set; }
-                public int hashCode => value?.GetHashCode() ?? 0;
-                public string paramterName { get; set; }
-            }
-        }
-
-        public ExpressionNode ConvertToData(Expression expression, Expression parameterExpression = null)
-        {
-            var arg = new DataConvertArgument { convertService = this };
-
-            if (parameterExpression is ConstantExpression constant)
-            {
-                arg.parameters = new List<DataConvertArgument.ParamterInfo> { new DataConvertArgument.ParamterInfo { value = constant.Value } };
-            }
-            return ConvertToData(arg, expression);
-        }
-
-        public ExpressionNode ConvertToData(LambdaExpression expression)
-        {
-            return ConvertToData(new DataConvertArgument { convertService = this }, expression);
-        }
-
-        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
-        {
-            if (expression is ConstantExpression constant)
-            {
-                if (constant.Value != null)
-                {
-                    var hashCode = constant.Value.GetHashCode();
-                    var param = arg.parameters?.FirstOrDefault(p => p.hashCode == hashCode);
-                    if (param != null)
-                    {
-                        return ExpressionNode.Member(parameterName: param.paramterName);
-                    }
-                }
-                return ExpressionNode.Constant(value: constant.Value, type: expression.Type);
-            }
-            else if (expression is IndexExpression index)
-            {
-                var left = ConvertToData(arg, index.Object);
-                var right = ConvertToData(arg, index.Arguments[0]);
-                return ExpressionNode.ArrayIndex(left: left, right: right);
-            }
-            else if (expression is ParameterExpression parameter)
-            {
-                return ExpressionNode.Member(parameterName: parameter.Name);
-            }
-            else if (expression is MemberExpression member)
-            {
-                return ConvertExpression(arg, member);
-            }
-            else if (expression is BinaryExpression binary)
-            {
-                return ConvertExpression(arg, binary);
-            }
-            else if (expression is UnaryExpression unary)
-            {
-                return ConvertExpression(arg, unary);
-            }
-            else if (expression is LambdaExpression lambda)
-            {
-                var parameterNames = lambda.Parameters.Select(p => p.Name).ToArray();
-                var body = ConvertToData(arg, lambda.Body);
-                return ExpressionNode.Lambda(parameterNames: parameterNames, body: body);
-            }
-            else if (expression is MethodCallExpression methodCall)
-            {
-                return ConvertExpression(arg, methodCall);
-            }
-            throw new NotSupportedException($"Unsupported expression type:{expression.GetType()}");
+            return ConvertToData(expression, out _, autoReduce);
         }
 
-        #region Member
-        ExpressionNode ConvertExpression(DataConvertArgument arg, MemberExpression member)
+        public ExpressionNode ConvertToData(Expression expression, out ParamterInfo[] parameters, bool autoReduce = false)
         {
-            var name = member.Member.Name;
-            if (member.Expression == null)
-                throw new NotSupportedException($"Unsupported MemberExpression : {member.Member?.Name}");
-
-            if (member.Expression is ParameterExpression parameter)
-            {
-                return ExpressionNode.Member(memberName: name, parameterName: parameter.Name);
-            }
+            var arg = new DataConvertArgument { convertService = this, autoReduce = autoReduce };
 
-            var objectValue = ConvertToData(arg, member.Expression);
-            if (objectValue.nodeType == NodeType.Constant)
-            {
-                var value = GetValue(arg, member);
-                return ExpressionNode.Constant(value: value, type: member.Type);
-            }
-            return ExpressionNode.Member(memberName: name, objectValue: objectValue);
-        }
+            ExpressionNode body = ConvertToData(arg, expression);
 
-
-        object GetValue(DataConvertArgument arg, Expression expression)
-        {
-            if (expression is ConstantExpression constant)
-            {
-                return constant.Value;
-            }
-            else if (expression is UnaryExpression unary)
-            {
-                if (ExpressionType.Convert == unary.NodeType)
-                {
-                    var del = Expression.Lambda(unary).Compile();
-                    var value = del.DynamicInvoke();
-                    return value;
-                }
-            }
-            else if (expression is LambdaExpression lambda)
-            {
-                var del = lambda.Compile();
-                var value = del.DynamicInvoke();
-                return value;
-            }
-            else if (expression is MemberExpression member)
-            {
-                if (ExpressionType.MemberAccess == member.NodeType)
-                {
-                    var del = Expression.Lambda(member).Compile();
-                    var value = del.DynamicInvoke();
-                    return value;
-                }
-            }
-            else if (expression is MethodCallExpression methodCall)
-            {
-                var del = Expression.Lambda(methodCall).Compile();
-                var value = del.DynamicInvoke();
-                return value;
-            }
-            else if (expression is BinaryExpression binary)
-            {
-                if (ExpressionType.ArrayIndex == binary.NodeType)
-                {
-                    var array = GetValue(arg, binary.Left) as IEnumerable;
-                    var index = GetValue(arg, binary.Right) as int? ?? 0;
-                    var enumerator = array.GetEnumerator();
-                    for (var t = 0; t <= index; t++) { enumerator.MoveNext(); }
-                    var value = enumerator.Current;
-                    return value;
-                }
-            }
-            throw new NotSupportedException($"GetValue failed, Unsupported expression type : {expression.GetType()}");
+            arg.GenerateGlobalParameterName();
+            parameters = arg.globalParameters?.ToArray();
+            var parameterNames = parameters?.Select(m => m.parameterName).ToArray();
+            return ExpressionNode.Lambda(parameterNames: parameterNames, body: body);
         }
 
-        string GetMemberName(DataConvertArgument arg, Expression expression)
+        public  ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
         {
-            if (expression is ParameterExpression parameter)
+            foreach (var expressionConvertor in expressionConvertors)
             {
-                // top level, no need to return parameterName
-                return null;
-                // return parameter.Name
-            }
-            else if (expression is MemberExpression member)
-            {
-                // get nested member
-                var name = member.Member.Name;
-                if (member.Expression == null) return name;
-                string parentName = GetMemberName(arg, member.Expression);
-                return parentName == null ? name : $"{parentName}.{name}";
-            }
-            else if (expression is UnaryExpression unary)
-            {
-                if (ExpressionType.Quote == unary.NodeType)
-                {
-                    return GetMemberName(arg, unary.Operand);
-                }
-            }
-            else if (expression is LambdaExpression lambda)
-            {
-                return GetMemberName(arg, lambda.Body);
+                var node = expressionConvertor.ConvertToData(arg, expression);
+                if (node != null) return node;
             }
 
-            throw new NotSupportedException($"GetMemberName failed, Unsupported expression type: {expression.GetType()}");
+            throw new NotSupportedException($"Unsupported expression type: {expression.GetType()}");
         }
-        #endregion
 
-        ExpressionNode ConvertExpression(DataConvertArgument arg, UnaryExpression unary)
-        {
-            switch (unary.NodeType)
-            {
-                case ExpressionType.Not:
-                    return ExpressionNode.Not(body: ConvertToData(unary.Operand));
-                case ExpressionType.Convert:
-                    return ExpressionNode.Convert(valueType: ComponentModel.ValueType.FromType(unary.Type), body: ConvertToData(unary.Operand));
-                case ExpressionType.Quote:
-                    return ConvertToData(arg, unary.Operand);
-            }
 
-            throw new NotSupportedException($"Unsupported unary NodeType : {unary.NodeType}");
-        }
-
-        ExpressionNode ConvertExpression(DataConvertArgument arg, BinaryExpression binary)
-        {
-            var left = ConvertToData(arg, binary.Left);
-            var right = ConvertToData(arg, binary.Right);
 
-            if (left?.nodeType == NodeType.Constant && right?.nodeType == NodeType.Constant)
-            {
-                var value = GetValue(arg, binary);
-                return ExpressionNode.Constant(value: value, type: binary.Type);
-            }
-            switch (binary.NodeType)
-            {
-                case ExpressionType.Equal: return ExpressionNode.Equal(left: left, right: right);
-                case ExpressionType.NotEqual: return ExpressionNode.NotEqual(left: left, right: right);
-                case ExpressionType.GreaterThan: return ExpressionNode.GreaterThan(left: left, right: right);
-                case ExpressionType.GreaterThanOrEqual: return ExpressionNode.GreaterThanOrEqual(left: left, right: right);
-                case ExpressionType.LessThan: return ExpressionNode.LessThan(left: left, right: right);
-                case ExpressionType.LessThanOrEqual: return ExpressionNode.LessThanOrEqual(left: left, right: right);
-                case ExpressionType.AndAlso: return ExpressionNode.And(left: left, right: right);
-                case ExpressionType.OrElse: return ExpressionNode.Or(left: left, right: right);
-                case ExpressionType.ArrayIndex: return ExpressionNode.ArrayIndex(left: left, right: right);
-            }
-            throw new NotSupportedException($"Unsupported binary NodeType : {binary.NodeType}");
-        }
     }
 }

+ 36 - 1
src/Vit.Linq/ExpressionTree/ExpressionConvertService.cs

@@ -1,11 +1,46 @@
 using System;
 using System.Collections.Generic;
-using System.Text;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.ExpressionConvertor;
+using Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls;
 
 namespace Vit.Linq.ExpressionTree
 {
     public partial class ExpressionConvertService
     {
         public static ExpressionConvertService Instance = new ExpressionConvertService();
+ 
+        public ExpressionConvertService()
+        {
+            // populate ExpressionConvertor
+            {
+                var types = GetType().Assembly.GetTypes().Where(type => type.IsClass
+                        && !type.IsAbstract
+                        && typeof(IExpressionConvertor).IsAssignableFrom(type)
+                        && type.GetConstructor(Type.EmptyTypes) != null
+                ).ToList();
+
+                types.ForEach(type => AddExpresssionConvertor(Activator.CreateInstance(type) as IExpressionConvertor));
+            }
+        }
+
+        protected List<IExpressionConvertor> expressionConvertors = new List<IExpressionConvertor>();
+        public virtual void AddExpresssionConvertor(IExpressionConvertor convertor)
+        {
+            expressionConvertors.Add(convertor);
+        }
+
+
+
+
+
+        public virtual void RegisterMethodConvertor(IMethodConvertor convertor)
+        {
+            var methodCallConvertor = expressionConvertors.FirstOrDefault(m => m is MethodCall) as MethodCall;
+            methodCallConvertor?.RegisterMethodConvertor(convertor);
+        }
+
+
     }
 }

+ 108 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Binary.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Collections;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+    public class Binary : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is IndexExpression index)
+            {
+                var left = arg.convertService.ConvertToData(arg, index.Object);
+                var right = arg.convertService.ConvertToData(arg, index.Arguments[0]);
+                return ExpressionNode.ArrayIndex(left: left, right: right);
+            }
+            else if (expression is BinaryExpression binary)
+            {
+                var left = arg.convertService.ConvertToData(arg, binary.Left);
+                var right = arg.convertService.ConvertToData(arg, binary.Right);
+
+                switch (binary.NodeType)
+                {
+                    case ExpressionType.AndAlso: return ExpressionNode.And(left: left, right: right);
+                    case ExpressionType.OrElse: return ExpressionNode.Or(left: left, right: right);
+
+                    //case ExpressionType.ArrayIndex: return ExpressionNode.ArrayIndex(left: left, right: right);
+
+                    //case ExpressionType.Equal: return ExpressionNode.Equal(left: left, right: right);
+                    //case ExpressionType.NotEqual: return ExpressionNode.NotEqual(left: left, right: right);
+                    //case ExpressionType.GreaterThan: return ExpressionNode.GreaterThan(left: left, right: right);
+                    //case ExpressionType.GreaterThanOrEqual: return ExpressionNode.GreaterThanOrEqual(left: left, right: right);
+                    //case ExpressionType.LessThan: return ExpressionNode.LessThan(left: left, right: right);
+                    //case ExpressionType.LessThanOrEqual: return ExpressionNode.LessThanOrEqual(left: left, right: right);
+
+                    //case ExpressionType.Add: return new ExpressionNode { nodeType = binary.NodeType.ToString(), expressionType = "Binary", left = left, right = right };
+                    case ExpressionType.Add:
+                        {
+                            ComponentModel.ValueType valueType = null;
+                            if (binary.Left.Type == typeof(string) || binary.Right.Type == typeof(string))
+                                valueType = ComponentModel.ValueType.FromType(typeof(string));
+                            return new ExpressionNode { nodeType = binary.NodeType.ToString(), expressionType = "Binary", left = left, right = right, valueType = valueType };
+                        }
+
+                    default: return new ExpressionNode { nodeType = binary.NodeType.ToString(), expressionType = "Binary", left = left, right = right };
+                }
+                throw new NotSupportedException($"Unsupported binary operator: {binary.NodeType}");
+            }
+            return null;
+        }
+
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.expressionType != "Binary") return null;
+
+            var left = arg.convertService.ToExpression(arg, data.left);
+            var right = arg.convertService.ToExpression(arg, data.right);
+
+            switch (data.nodeType)
+            {
+                case NodeType.And:
+                    return Expression.AndAlso(left, right);
+
+                case NodeType.Or:
+                    return Expression.OrElse(left, right);
+
+                case NodeType.ArrayIndex:
+                    return Expression.ArrayIndex(left, right);
+
+                //case NodeType.Equal: return Expression.Equal(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
+                //case NodeType.NotEqual: return Expression.NotEqual(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
+                //case NodeType.LessThan: return Expression.LessThan(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
+                //case NodeType.LessThanOrEqual: return Expression.LessThanOrEqual(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
+                //case NodeType.GreaterThan: return Expression.GreaterThan(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
+                //case NodeType.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(ToExpression(arg, data.left) ?? Expression.Constant(null), ToExpression(arg, data.right) ?? Expression.Constant(null));
+
+                case nameof(ExpressionType.Add):
+                    {
+                        left ??= Expression.Constant(null);
+                        right ??= Expression.Constant(null);
+                        if (left.Type == typeof(string) || right.Type == typeof(string))
+                        {
+                            var method = typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) });
+                            return Expression.Call(method, new[] { left, right });
+                        }
+                        return Expression.Add(left, right);
+                    }
+                default:
+                    {
+                        left ??= Expression.Constant(null);
+                        right ??= Expression.Constant(null);
+
+                        var method = typeof(Expression).GetMethod(data.nodeType, new[] { typeof(Expression), typeof(Expression) });
+                        return method?.Invoke(null, new object[] { left, right }) as Expression;
+                    }
+            }
+
+        }
+
+
+
+    }
+}

+ 38 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Binarys/Coalesce.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Text;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.Binarys
+{
+
+    public class Coalesce : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            // a??b
+            if (expression is BinaryExpression binary && binary.NodeType == ExpressionType.Coalesce)
+            {
+                var left = arg.convertService.ConvertToData(arg, binary.Left);
+                var right = arg.convertService.ConvertToData(arg, binary.Right);
+
+                return ExpressionNode.Binary(nameof(ExpressionType.Coalesce), left: left, right: right);
+            }
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            // a??b
+            if (data.nodeType == ExpressionType.Coalesce.ToString())
+            {
+                var left = arg.convertService.ToExpression(arg, data.left) ?? Expression.Constant(null);
+                var right = arg.convertService.ToExpression(arg, data.right) ?? Expression.Constant(null);
+                return Expression.Coalesce(left, right);
+            }
+            return null;
+        }
+    }
+}

+ 47 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Conditional.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Text;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+    /// <summary>
+    ///  a > b ? a : b
+    ///  Node arguments: [  condition, valueForTrue, valueForFalse ]
+    /// </summary>
+    public class Conditional : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is ConditionalExpression conditionExp)
+            {
+                var condition = arg.convertService.ConvertToData(arg, conditionExp.Test);
+                var valueForTrue = arg.convertService.ConvertToData(arg, conditionExp.IfTrue);
+                var valueForFalse = arg.convertService.ConvertToData(arg, conditionExp.IfFalse);
+
+                return new ExpressionNode
+                {
+                    nodeType = "Conditional",
+                    arguments = new ExpressionNode[] { condition, valueForTrue, valueForFalse }
+                };
+            }
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.nodeType == "Conditional")
+            {
+                var condition = arg.convertService.ToExpression(arg, data.arguments[0]);
+                var valueForTrue = arg.convertService.ToExpression(arg, data.arguments[1]);
+                var valueForFalse = arg.convertService.ToExpression(arg, data.arguments[2]);
+
+                return Expression.Condition(condition, valueForTrue, valueForFalse);
+            }
+            return null;
+        }
+    }
+}

+ 83 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Constant.cs

@@ -0,0 +1,83 @@
+using System;
+using System.Collections;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+    public class Constant : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is ConstantExpression constant)
+            {
+                var type = expression.Type;
+                var value = constant.Value;
+                if (value != null && !IsTransportableType(type))
+                {
+                    return arg.GetParameter(value, type);
+                }
+                return ExpressionNode.Constant(value: constant.Value, type: type);
+            }
+
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.nodeType != NodeType.Constant) return null;
+
+            ExpressionNode_Constant constant = data;
+            var value = constant.value;
+            Type targetType = constant.valueType?.ToType();
+
+            if (targetType == null) return Expression.Constant(value);
+
+            if (value != null)
+            {
+                value = ComponentModel.ValueType.ConvertToType(value, targetType);
+            }
+            return Expression.Constant(value, targetType);
+        }
+
+
+
+        static bool IsTransportableType(Type type)
+        {
+            if (IsBasicType(type)) return true;
+
+            if (type.IsArray && IsTransportableType(type.GetElementType()))
+            {
+                return true;
+            }
+
+            if (type.IsGenericType)
+            {
+                if (type.GetGenericArguments().Any(t => !IsTransportableType(t))) return false;
+
+                if (typeof(IList).IsAssignableFrom(type)
+                    || typeof(ICollection).IsAssignableFrom(type)
+                    )
+                    return true;
+            }
+
+            return false;
+
+            #region Method IsBasicType
+            // is valueType of Nullable 
+            static bool IsBasicType(Type type)
+            {
+                return
+                    type.IsEnum || // enum
+                    type == typeof(string) || // string
+                    type.IsValueType ||  //int
+                    (type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition()); // int?
+            }
+            #endregion
+        }
+
+    }
+}

+ 56 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Lambda.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+    public class Lambda : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is LambdaExpression lambda)
+            {
+                var parameterNames = lambda.Parameters.Select(parameter => parameter.Name).ToArray();
+                arg.RegisterParameterNames(parameterNames);
+
+                var body = arg.convertService.ConvertToData(arg, lambda.Body);
+                return ExpressionNode.Lambda(parameterNames: parameterNames, body: body);
+            }
+
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.nodeType != NodeType.Lambda) return null;
+
+            ExpressionNode_Lambda lambda = data;
+
+            string[] parameterNames = lambda.parameterNames ?? new string[0];
+
+            Type[] paramTypes = lambda.Lambda_GetParamTypes();
+            paramTypes ??= new Type[0];
+
+            var parameters = parameterNames.Select((name, i) =>
+            {
+                var type = paramTypes.Length > i ? paramTypes[i] : typeof(object);
+
+                if (string.IsNullOrWhiteSpace(name))
+                    return Expression.Parameter(type);
+                return Expression.Parameter(type, name);
+            }).ToArray();
+
+            arg = arg.WithParams(parameters);
+            var expression = arg.convertService.ToExpression(arg, lambda.body);
+            if (expression == null)
+            {
+                return null;
+            }
+            return Expression.Lambda(expression, parameters);
+        }
+
+    }
+}

+ 70 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Member.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Data;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Xml.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+    public class Member : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is MemberExpression member)
+            {
+                if (typeof(IQueryable).IsAssignableFrom(member.Type) && arg.ReduceValue(member, out object query))
+                {
+                    var value = query;
+                    var type = expression.Type;
+                    if (value != null)
+                    {
+                        return arg.GetParameter(value, type);
+                    }
+                }
+
+                var name = member.Member?.Name;
+                if (member.Expression == null)
+                    throw new NotSupportedException($"Unsupported MemberExpression : {name}");
+
+                if (member.Expression is ParameterExpression parameter)
+                    return ExpressionNode.Member(parameterName: parameter.Name, memberName: name).Member_SetType(expression.Type);
+
+                var objectValue = arg.convertService.ConvertToData(arg, member.Expression);
+                return ExpressionNode.Member(objectValue: objectValue, memberName: name).Member_SetType(expression.Type);
+            }
+            else if (expression is ParameterExpression parameter)
+            {
+                return ExpressionNode.Member(parameterName: parameter.Name, memberName: null).Member_SetType(expression.Type);
+            }
+
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.nodeType != NodeType.Member) return null;
+
+            ExpressionNode_Member member = data;
+
+            if (member.objectValue == null)
+            {
+                ParameterExpression paramExp = null;
+                if (member.parameterName != null) paramExp = arg.parameters.FirstOrDefault(p => p.Name == member.parameterName);
+
+                //if (paramExp == null) paramExp = arg.parameters.First();
+                if (paramExp == null) throw new NotSupportedException($"can not find parameter : {member.parameterName}");
+
+                return LinqHelp.GetFieldMemberExpression(paramExp, member.memberName);
+            }
+            else
+            {
+                var instanceExp = arg.convertService.ToExpression(arg, member.objectValue);
+                return LinqHelp.GetFieldMemberExpression(instanceExp, member.memberName);
+            }
+        }
+
+    }
+}

+ 107 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCall.cs

@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+
+    /*
+Supported methods:
+
+#1. Contains
+string.Contains
+List<>.Contains
+Queryable.Contains
+Enumerable.Contains
+
+#2. IsNullOrEmpty
+string.IsNullOrEmpty
+
+#3. ElementAt
+Enumerable.ElementAt
+
+
+
+#10 Contain IsNullOrEmpty ElementAt
+
+#11. Take
+#12. Skip
+
+
+
+#80 Where
+Queryable.Where
+
+#81 Order
+Queryable.OrderBy OrderByDescending ThenBy ThenByDescending
+
+#82 Any
+Queryable.Any
+
+
+
+#99. InstanceMethod
+String.StartsWith
+String.EndsWith
+
+     */
+
+
+    public class MethodCall : IExpressionConvertor
+    {
+        protected List<IMethodConvertor> methodConvertors = new List<IMethodConvertor>();
+
+        public virtual void RegisterMethodConvertor(IMethodConvertor convertor)
+        {
+            methodConvertors.Add(convertor);
+
+            methodConvertors.Sort((a, b) => a.priority - b.priority);
+        }
+
+        public MethodCall()
+        {
+            // populate MethodConvertor
+            var types = GetType().Assembly.GetTypes().Where(type => type.IsClass
+                    && !type.IsAbstract
+                    && typeof(IMethodConvertor).IsAssignableFrom(type)
+                    && type.GetConstructor(Type.EmptyTypes) != null
+            ).ToList();
+
+            types.ForEach(type => RegisterMethodConvertor(Activator.CreateInstance(type) as IMethodConvertor));
+        }
+
+
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is MethodCallExpression methodCall)
+            {
+                var convertor = methodConvertors.FirstOrDefault(m => m.PredicateToData(arg, methodCall));
+                if (convertor != null)
+                    return convertor.ToData(arg, methodCall);
+
+                throw new NotSupportedException($"Unsupported method call: {methodCall.Method.Name}");
+            }
+
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.nodeType != NodeType.MethodCall) return null;
+
+            ExpressionNode_MethodCall call = data;
+
+            var convertor = methodConvertors.FirstOrDefault(m => m.PredicateToCode(arg, call));
+            if (convertor != null)
+                return convertor.ToCode(arg, call);
+
+            throw new NotSupportedException($"Method not supported: {call.methodName}");
+        }
+
+    }
+}

+ 15 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/IMethodConvertor.cs

@@ -0,0 +1,15 @@
+using System.Linq.Expressions;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+    public interface IMethodConvertor
+    {
+        int priority { get; }
+        bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call);
+        Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call);
+
+        bool PredicateToData(DataConvertArgument arg, MethodCallExpression call);
+        ExpressionNode ToData(DataConvertArgument arg, MethodCallExpression call);
+    }
+}

+ 36 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/InstanceMethod.cs

@@ -0,0 +1,36 @@
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+    #region #99 InstanceMethod
+    // InstanceMethod:
+    //  String.StartsWith
+    //  String.EndsWith
+    public class InstanceMethod : MethodConvertor_Base
+    {
+        public override int priority => 10000;
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            return call.Object != null;
+            //return true;
+        }
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return call.@object != null;
+        }
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var instance = arg.convertService.ToExpression(arg, call.@object);
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            return Expression.Call(instance, call.methodName, null, methodArguments);
+        }
+    }
+    #endregion
+
+}

+ 63 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/MethodConvertor_Base.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+    public abstract class MethodConvertor_Base : IMethodConvertor
+    {
+        public virtual int priority { get; set; } = 100;
+        public abstract bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call);
+        public abstract Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call);
+
+        public abstract bool PredicateToData(DataConvertArgument arg, MethodCallExpression call);
+        public virtual ExpressionNode ToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            var method = call.Method;
+
+            // #1 typeName
+            var typeName = method.DeclaringType.Name;
+
+            // #2 Object
+            ExpressionNode @object = call.Object == null ? null : arg.convertService.ConvertToData(arg, call.Object);
+
+
+            // #3 methodName
+            var methodName = method.Name;
+
+            // #4 typeArguments
+            // var typeArguments = method.GetGenericArguments();
+
+            // #5 Arguments
+            var arguments = call.Arguments?.Select(param => arg.convertService.ConvertToData(arg, param)).ToArray();
+            if (arguments?.Any() == true)
+            {
+                var paramArray = method.GetParameters();
+                for (int i = Math.Min(arguments.Length, paramArray.Length) - 1; i >= 0; i--)
+                {
+                    if (arguments[i]?.nodeType == NodeType.Constant)
+                    {
+                        ExpressionNode_Constant constant = arguments[i];
+                        constant.valueType = ComponentModel.ValueType.FromType(paramArray[i].ParameterType);
+                    }
+                }
+            }
+
+
+            // if all arguments is constant, directly evaluate constant value
+            //var nodeTypes = new List<string> { instance?.nodeType };
+            //if (methodArguments != null) nodeTypes.AddRange(methodArguments.Select(m => m?.nodeType));
+            //if (nodeTypes.Where(nodeTypes => nodeTypes != null).All(nodeType => nodeType == ExpressionNodeType.Constant))
+            //{
+            //    var value = GetValue(call);
+            //    return ExpressionNode_Constant.FromValue(value);
+            //}
+
+            return ExpressionNode.MethodCall(typeName: typeName, methodName: methodName, @object: @object, arguments: arguments);
+        }
+
+
+
+    }
+}

+ 32 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/MethodConvertor_Common.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+
+    public abstract class MethodConvertor_Common : MethodConvertor_Base
+    {
+        public string methodName;
+        public virtual Type methodType { get => null; }
+
+        public MethodConvertor_Common(string methodName = null, int priority = 100)
+        {
+            if (string.IsNullOrWhiteSpace(methodName)) methodName = GetType().Name;
+            this.methodName = methodName;
+            this.priority = priority;
+        }
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            return call.Method.Name == methodName && (methodType == null || methodType == call.Method.DeclaringType);
+        }
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return call.methodName == methodName && (methodType == null || methodType.Name == call.typeName);
+        }
+    }
+
+}

+ 38 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/MethodConvertor_Delegate.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+
+    public class MethodConvertor_Delegate : MethodConvertor_Base
+    {
+        public string methodName;
+        /// <summary>
+        /// Expression ToCode(convertService, CodeConvertArgument arg, ExpressionNode_Call call)
+        /// </summary>
+        public Func<CodeConvertArgument, ExpressionNode_MethodCall, Expression> FuncToCode;
+        public MethodConvertor_Delegate(string methodName, Func<CodeConvertArgument, ExpressionNode_MethodCall, Expression> FuncToCode)
+        {
+            this.methodName = methodName;
+            this.FuncToCode = FuncToCode;
+        }
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            return call.Method.Name == methodName;
+        }
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return call.methodName == methodName;
+        }
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return FuncToCode(arg, call);
+        }
+    }
+
+}

+ 74 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Others.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using System.Collections.Generic;
+using System.Reflection;
+using Vit.Extensions.Linq_Extensions;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+
+
+    #region #10 Contain  ElementAt
+
+    public class Contains : MethodConvertor_Common
+    {
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var instance = arg.convertService.ToExpression(arg, call.@object);
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            // #1 instance Method
+            if (instance != null)
+            {
+                // ##1 string.Contains
+                // ##2 List<>.Contains
+                // # ...
+                return Expression.Call(instance, "Contains", null, methodArguments);
+            }
+
+
+            // #2 static Method
+            MethodInfo method;
+            var argType = methodArguments[0].Type;
+            var modelType = argType.GetGenericArguments()[0];
+            if (typeof(IQueryable).IsAssignableFrom(argType))
+            {
+                // Queryable.Contains                 
+                method = (new Func<IQueryable<string>, string, bool>(Queryable.Contains<string>))
+                            .Method.GetGenericMethodDefinition().MakeGenericMethod(modelType);
+            }
+            else
+            {
+                // Enumerable.Contains 
+                method = (new Func<IQueryable<string>, string, bool>(Enumerable.Contains<string>))
+                           .Method.GetGenericMethodDefinition().MakeGenericMethod(modelType);
+            }
+            return Expression.Call(method, methodArguments);
+        }
+
+    }
+
+
+    public class ElementAt : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(Enumerable);
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            //var instance = convertService.ToExpression(arg, call.instance);
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+
+            // Enumerable.ElementAt
+            var type = methodArguments[0].Type.GetGenericArguments()[0];
+            var method = (new Func<IEnumerable<string>, int, string>(Enumerable.ElementAt<string>))
+                            .Method.GetGenericMethodDefinition().MakeGenericMethod(type);
+            return Expression.Call(method, methodArguments);
+        }
+    }
+    #endregion
+
+
+
+}

+ 49 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Extensions_Methods/Queryable_Methods.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using System.Collections.Generic;
+using Vit.Extensions.Linq_Extensions;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Extensions_Methods
+{
+
+    public class Queryable_Methods : MethodConvertor_Base
+    {
+
+        public Type methodType { get; } = typeof(Queryable_Extensions);
+
+        static readonly List<string> methodNames = typeof(Queryable_Extensions).GetMethods().Select(m => m.Name).ToList();
+        public override int priority => 10000;
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            // is method from Queryable
+            return methodType == call.Method.DeclaringType;
+        }
+
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return methodNames.Contains(call.methodName) && methodType.Name == call.typeName;
+        }
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            if (call.arguments?.Count() == 1)
+            {
+                var source = arg.convertService.ToExpression(arg, call.arguments[0]);
+                var elementType = source.Type.GetGenericArguments()[0];
+
+                var methodArguments = new[] { source };
+
+                return Expression.Call(typeof(Queryable), call.methodName, new[] { elementType }, methodArguments);
+            }
+
+            throw new NotSupportedException($"Unsupported method typeName: {call.typeName}, methodName: {call.methodName}");
+        }
+    }
+
+
+
+}

+ 30 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Extensions_Methods/TotalCount.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Extensions.Linq_Extensions;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Extensions_Methods
+{
+
+    public class TotalCount : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(Queryable_Extensions);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var source = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var elementType = source.Type.GetGenericArguments()[0];
+
+            var methodArguments = new Expression[] { source };
+
+            var method = (new Func<IQueryable<string>, int>(Queryable_Extensions.TotalCount<string>))
+                            .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+            return Expression.Call(method, methodArguments);
+        }
+    }
+
+
+
+
+}

+ 58 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Any.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Extensions.Linq_Extensions;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    /// <summary>
+    ///  Queryable.Any
+    /// </summary>
+    public class Any : MethodConvertor_Common
+    {
+        // public static bool Any<TSource>(this IQueryable<TSource> source)
+        // public static bool Any<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
+
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            //var instance = convertService.ToExpression(arg, call.instance);
+            //var methodArguments = call.methodArguments?.Select(node => convertService.ToExpression(arg, node)).ToArray();
+
+            var expSource = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var elementType = expSource.Type.GetGenericArguments()[0];
+
+            // #1 public static bool Any<TSource>(this IQueryable<TSource> source)
+            if (call.arguments?.Length == 1)
+            {
+                var methodArguments = new[] { expSource };
+
+                var method = (new Func<IQueryable<string>, bool>(Queryable.Any<string>))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+                return Expression.Call(method, methodArguments);
+            }
+
+            // #2 public static bool Any<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
+            else if (call.arguments?.Length == 2)
+            {
+                var lambda = call.arguments[1] as ExpressionNode_Lambda;
+
+                var expPredicate = arg.convertService.ToLambdaExpression(arg, lambda, elementType);
+
+                var methodArguments = new[] { expSource, expPredicate };
+
+                var method = (new Func<IQueryable<string>, Expression<Func<string, bool>>, bool>(Queryable.Any<string>))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+                return Expression.Call(method, methodArguments);
+            }
+
+            throw new NotSupportedException($"Method not supported: {call.methodName}");
+        }
+    }
+
+
+
+}

+ 64 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Join.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    public class Join : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            //var instance = arg.convertService.ToExpression(arg, call.@object);
+            //var methodArguments = call.arguments?.Select(node => convertService.ToExpression(arg, node)).ToArray();
+
+            //   IQueryable<TResult> Join<TOuter, TInner, TKey, TResult>
+            //      (this IQueryable<TOuter> outer,
+            //          IEnumerable<TInner> inner,
+            //          Expression<Func<TOuter, TKey>> outerKeySelector,
+            //          Expression<Func<TInner, TKey>> innerKeySelector,
+            //          Expression<Func<TOuter, TInner, TResult>> resultSelector);
+            var outer = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var TOuter = outer.Type.GetGenericArguments()[0];
+
+            var inner = arg.convertService.ToExpression(arg, call.arguments[1]);
+            var TInner = inner.Type.GetGenericArguments()[0];
+
+            var outerKeySelector = arg.convertService.ToLambdaExpression(arg, call.arguments[2], TOuter);
+            var innerKeySelector = arg.convertService.ToLambdaExpression(arg, call.arguments[3], TInner);
+
+            var resultSelector = arg.convertService.ToLambdaExpression(arg, call.arguments[4], TOuter, TInner);
+
+            var TKey = outerKeySelector.ReturnType;
+            var TResult = resultSelector.ReturnType;
+
+
+            Type[] typeArguments = new[] { TOuter, TInner, TKey, TResult };
+            Expression[] arguments = new[] { outer, inner, outerKeySelector, innerKeySelector, resultSelector };
+
+
+
+            var method = (new Func<
+                IQueryable<string>,
+                IEnumerable<string>,
+                Expression<Func<string, string>>,
+                Expression<Func<string, string>>,
+                Expression<Func<string, string, string>>,
+                IQueryable<string>>(Queryable.Join)
+            ).Method.GetGenericMethodDefinition().MakeGenericMethod(typeArguments);
+
+            return Expression.Call(method, arguments);
+        }
+
+
+
+
+    }
+
+
+}

+ 73 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/OrderBy.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Linq;
+using System.Reflection;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    /// <summary>
+    ///  Queryable.OrderBy OrderByDescending ThenBy ThenByDescending
+    /// </summary>
+    public class OrderBy : MethodConvertor_Base
+    {
+
+        public Type methodType { get; } = typeof(Queryable);
+
+        static readonly List<string> methodNames = new List<string> { "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending" };
+
+        // public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
+        // public static IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
+        // public static IOrderedQueryable<TSource> ThenBy<TSource, TKey>(this IOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector); 
+        // public static IOrderedQueryable<TSource> ThenByDescending<TSource, TKey>(this IOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector); 
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            return  methodNames.Contains(call.Method.Name) && methodType == call.Method.DeclaringType;
+        }
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return methodNames.Contains(call.methodName) && methodType.Name == call.typeName;
+        }
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var expSource = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var elementType = expSource.Type.GetGenericArguments()[0];
+
+            var lambda = call.arguments[1] as ExpressionNode_Lambda;
+            var expKeySelector = arg.convertService.ToLambdaExpression(arg, lambda, elementType);
+            var keyType = expKeySelector.ReturnType;
+
+            MethodInfo method = null;
+
+            switch (call.methodName)
+            {
+                case "OrderBy": // public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector); 
+                    method = (new Func<IQueryable<object>, Expression<Func<object, string>>, IOrderedQueryable<object>>(Queryable.OrderBy<object, string>))
+                        .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType, keyType);
+                    break;
+                case "OrderByDescending": // public static IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector); 
+                    method = (new Func<IQueryable<object>, Expression<Func<object, string>>, IOrderedQueryable<object>>(Queryable.OrderByDescending<object, string>))
+                        .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType, keyType);
+                    break;
+                case "ThenBy": // public static IOrderedQueryable<TSource> ThenBy<TSource, TKey>(this IOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector); 
+                    method = (new Func<IOrderedQueryable<object>, Expression<Func<object, string>>, IOrderedQueryable<object>>(Queryable.ThenBy<object, string>))
+                        .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType, keyType);
+                    break;
+                case "ThenByDescending": // public static IOrderedQueryable<TSource> ThenByDescending<TSource, TKey>(this IOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector); 
+                    method = (new Func<IOrderedQueryable<object>, Expression<Func<object, string>>, IOrderedQueryable<object>>(Queryable.ThenByDescending<object, string>))
+                        .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType, keyType);
+                    break;
+            }
+
+            var methodArguments = new[] { expSource, expKeySelector };
+            return Expression.Call(method, methodArguments);
+        }
+    }
+
+
+}

+ 48 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Queryable_Methods.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using System.Collections.Generic;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    public class Queryable_Methods : MethodConvertor_Base
+    {
+
+        public Type methodType { get; } = typeof(Queryable);
+
+        static readonly List<string> methodNames = typeof(Queryable).GetMethods().Select(m => m.Name).ToList();
+        public override int priority => 10000;
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            // is method from Queryable
+            return methodType == call.Method.DeclaringType;
+        }
+
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return methodNames.Contains(call.methodName) && methodType.Name == call.typeName;
+        }
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            if (call.arguments?.Count() == 1)
+            {
+                var source = arg.convertService.ToExpression(arg, call.arguments[0]);
+                var elementType = source.Type.GetGenericArguments()[0];
+
+                var methodArguments = new[] { source };
+
+                return Expression.Call(typeof(Queryable), call.methodName, new[] { elementType }, methodArguments);
+            }
+
+            throw new NotSupportedException($"Unsupported method typeName: {call.typeName}, methodName: {call.methodName}");
+        }
+    }
+
+
+
+}

+ 60 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Select.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+    public class Select : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var source = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var elementType = source.Type.GetGenericArguments()[0];
+            var nodeSelector = call.arguments[1] as ExpressionNode_Lambda;
+
+            // #1 IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
+            if (nodeSelector?.parameterNames?.Length == 1)
+            {
+                var resultSelector = arg.convertService.ToLambdaExpression(arg, nodeSelector, elementType);
+
+                var TResult = resultSelector.ReturnType;
+
+                var methodArguments = new[] { source, resultSelector };
+
+                var method = (new Func<
+                                    IQueryable<string>,
+                                    Expression<Func<string, string>>,
+                                    IQueryable<string>
+                                >(Queryable.Select))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType, TResult);
+                return Expression.Call(method, methodArguments);
+            }
+
+            // #2 IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, int, TResult>> selector)
+            if (nodeSelector?.parameterNames?.Length == 2)
+            {
+                var resultSelector = arg.convertService.ToLambdaExpression(arg, nodeSelector, elementType, typeof(int));
+
+                var TResult = resultSelector.ReturnType;
+
+                var methodArguments = new[] { source, resultSelector };
+
+                var method = (new Func<
+                                    IQueryable<string>,
+                                    Expression<Func<string, int, string>>,
+                                    IQueryable<string>
+                                 >(Queryable.Select))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType, TResult);
+                return Expression.Call(method, methodArguments);
+            }
+
+            throw new NotSupportedException($"Method not supported: {call.methodName}");
+        }
+    }
+
+
+
+}

+ 55 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/SelectMany.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    public class SelectMany : MethodConvertor_Common
+    {
+
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+
+            //   IQueryable<TResult> SelectMany<TSource, TCollection, TResult>(
+            //      this IQueryable<TSource> source,
+            //      Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector,
+            //      Expression<Func<TSource, TCollection, TResult>> resultSelector
+            //   )
+            var source = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var TSource = source.Type.GetGenericArguments()[0];
+
+            var collectionSelector = arg.convertService.ToExpression(arg, call.arguments[1]);
+            var TCollection = collectionSelector.Type.GetGenericArguments()[0];
+
+            var resultSelector = arg.convertService.ToLambdaExpression(arg, call.arguments[2], TSource, TCollection);
+
+            var TResult = resultSelector.ReturnType;
+
+
+            Type[] typeArguments = new[] { TSource, TCollection, TResult };
+            Expression[] arguments = new[] { source, collectionSelector, resultSelector };
+
+
+            var method = (new Func<
+                IQueryable<string>, 
+                Expression<Func<string, IEnumerable<string>>>,
+                Expression<Func<string, string, string>>,
+                IQueryable<string>>(Queryable.SelectMany)
+            ).Method.GetGenericMethodDefinition().MakeGenericMethod(typeArguments);
+
+            return Expression.Call(method, arguments);
+        }
+
+
+
+
+    }
+
+
+}

+ 31 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Skip.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using System.Reflection;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    public class Skip : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            MethodInfo method;
+            var argType = methodArguments[0].Type;
+            var modelType = argType.GetGenericArguments()[0];
+
+            // Queryable.Skip
+            method = (new Func<IQueryable<string>, int, IQueryable<string>>(Queryable.Skip<string>))
+                            .Method.GetGenericMethodDefinition().MakeGenericMethod(modelType);
+
+            return Expression.Call(method, methodArguments);
+        }
+
+    }
+
+}

+ 31 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Take.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using System.Reflection;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    public class Take : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            MethodInfo method;
+            var argType = methodArguments[0].Type;
+            var modelType = argType.GetGenericArguments()[0];
+
+            // Queryable.Take
+            method = (new Func<IQueryable<string>, int, IQueryable<string>>(Queryable.Take<string>))
+                                    .Method.GetGenericMethodDefinition().MakeGenericMethod(modelType);
+
+            return Expression.Call(method, methodArguments);
+        }
+
+    }
+
+}

+ 55 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/Queryable_Methods/Where.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Queryable_Methods
+{
+
+    /// <summary>
+    ///  Queryable.Where
+    /// </summary>
+    public class Where : MethodConvertor_Common
+    {
+        // public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
+        // public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, int, bool>> predicate)
+        public override Type methodType { get; } = typeof(Queryable);
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            var expSource = arg.convertService.ToExpression(arg, call.arguments[0]);
+            var elementType = expSource.Type.GetGenericArguments()[0];
+
+            var lambda = call.arguments[1] as ExpressionNode_Lambda;
+
+            // #1 IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
+            if (lambda?.parameterNames?.Length == 1)
+            {
+                var expPredicate = arg.convertService.ToLambdaExpression(arg, lambda, elementType);
+
+                var methodArguments = new[] { expSource, expPredicate };
+
+                var method = (new Func<IQueryable<string>, Expression<Func<string, bool>>, IQueryable<string>>(Queryable.Where<string>))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+                return Expression.Call(method, methodArguments);
+            }
+
+            // #2 IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, int, bool>> predicate)
+            else if (lambda?.parameterNames?.Length == 2)
+            {
+                var expPredicate = arg.convertService.ToLambdaExpression(arg, lambda, elementType, typeof(int));
+
+                var methodArguments = new[] { expSource, expPredicate };
+
+                var method = (new Func<IQueryable<string>, Expression<Func<string, int, bool>>, IQueryable<string>>(Queryable.Where<string>))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+                return Expression.Call(method, methodArguments);
+            }
+
+            throw new NotSupportedException($"Method not supported: {call.methodName}");
+        }
+    }
+
+
+}

+ 73 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/MethodCalls/String_Methods.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls
+{
+
+    /// <summary>
+    /// String.IsNullOrEmpty
+    /// </summary>
+    public class IsNullOrEmpty : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(string);
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            //var instance = convertService.ToExpression(arg, call.instance);
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            // string.IsNullOrEmpty
+            return Expression.Call(typeof(string), "IsNullOrEmpty", null, methodArguments);
+        }
+    }
+
+
+    /// <summary>
+    /// String.Format
+    /// </summary>
+    public class Format : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(string);
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+
+            //var instance = convertService.ToExpression(arg, call.instance);
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            // string.IsNullOrEmpty
+            return Expression.Call(typeof(string), "Format", null, methodArguments);
+        }
+    }
+
+
+    /// <summary>
+    /// String.Concat
+    /// </summary>
+    public class Concat : MethodConvertor_Common
+    {
+        public override Type methodType { get; } = typeof(string);
+
+        public override bool PredicateToData(DataConvertArgument arg, MethodCallExpression call)
+        {
+            return (call.Method.Name == "Add"|| call.Method.Name == "Concat") && methodType == call.Method.DeclaringType;
+        }
+
+        public override bool PredicateToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            return (call.methodName == "Add"|| call.methodName == "Concat") && (methodType == null || methodType.Name == call.typeName);
+        }
+
+        public override Expression ToCode(CodeConvertArgument arg, ExpressionNode_MethodCall call)
+        {
+            //var instance = convertService.ToExpression(arg, call.instance);
+            var methodArguments = call.arguments?.Select(node => arg.convertService.ToExpression(arg, node)).ToArray();
+
+            // string.IsNullOrEmpty
+            return Expression.Call(typeof(string), "Concat", null, methodArguments);
+        }
+    }
+
+}

+ 89 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/ModelGenerator.cs

@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+using System.Reflection;
+using System.Text;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+    /*
+// https://www.cnblogs.com/sesametech-dotnet/p/13176329.html
+// https://learn.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.constructorbuilder?view=net-8.0
+
+<ItemGroup>
+    <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
+    <PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
+</ItemGroup>
+     */
+
+    public class ModelGenerator
+    {
+        public static Type CreateType(Dictionary<string, Type> properties)
+        {
+            // #1 define Type 
+            var assName = Guid.NewGuid().ToString();
+            var moduleName = "Main";
+            var typeName = "DynamicClass";
+            var assemblyName = new AssemblyName(assName);
+            var dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
+            var dynamicModule = dynamicAssembly.DefineDynamicModule(moduleName);
+            var typeBuilder = dynamicModule.DefineType(typeName,
+                    TypeAttributes.Public |
+                    TypeAttributes.Class |
+                    TypeAttributes.AutoClass |
+                    TypeAttributes.AnsiClass |
+                    TypeAttributes.BeforeFieldInit |
+                    TypeAttributes.AutoLayout,
+                    null);     // This is the type of class to derive from. Use null if there isn't one
+            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public |
+                                                MethodAttributes.SpecialName |
+                                                MethodAttributes.RTSpecialName);
+
+            // #2 Add Property
+            //string propertyName = null;
+            //Type propertyType = null;
+            //AddProperty(dynamicType, propertyName, propertyType);
+            foreach (var kv in properties)
+            {
+                AddProperty(typeBuilder, kv.Key, kv.Value);
+            }
+
+            var generatedType = typeBuilder.CreateTypeInfo().AsType();
+            return generatedType;
+        }
+        private static void AddProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
+        {
+            var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
+            var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
+
+            var getMethod = typeBuilder.DefineMethod("get_" + propertyName,
+                MethodAttributes.Public |
+                MethodAttributes.SpecialName |
+                MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
+            var getMethodIL = getMethod.GetILGenerator();
+            getMethodIL.Emit(OpCodes.Ldarg_0);
+            getMethodIL.Emit(OpCodes.Ldfld, fieldBuilder);
+            getMethodIL.Emit(OpCodes.Ret);
+
+            var setMethod = typeBuilder.DefineMethod("set_" + propertyName,
+                  MethodAttributes.Public |
+                  MethodAttributes.SpecialName |
+                  MethodAttributes.HideBySig,
+                  null, new[] { propertyType });
+            var setMethodIL = setMethod.GetILGenerator();
+            Label modifyProperty = setMethodIL.DefineLabel();
+            Label exitSet = setMethodIL.DefineLabel();
+
+            setMethodIL.MarkLabel(modifyProperty);
+            setMethodIL.Emit(OpCodes.Ldarg_0);
+            setMethodIL.Emit(OpCodes.Ldarg_1);
+            setMethodIL.Emit(OpCodes.Stfld, fieldBuilder);
+            setMethodIL.Emit(OpCodes.Nop);
+            setMethodIL.MarkLabel(exitSet);
+            setMethodIL.Emit(OpCodes.Ret);
+
+            propertyBuilder.SetGetMethod(getMethod);
+            propertyBuilder.SetSetMethod(setMethod);
+        }
+    }
+}

+ 133 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/New.cs

@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Xml.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+    public class New : IExpressionConvertor
+    {
+
+        List<MemberBind> GetConstractorArgs(DataConvertArgument arg, NewExpression newExp)
+        {
+            List<MemberBind> constructorArgs;
+
+            if (newExp.Members == null)
+            {
+                // #1 constructor   NewExpression{args=[v1,v2]]} -> new(1,2)
+                constructorArgs = newExp.Arguments?.Select(value => new MemberBind { name = null, value = arg.convertService.ConvertToData(arg, value) }).ToList();
+            }
+            else
+            {
+                // #2 constructor and memberInit    NewExpression{args=[v1,v2],members=["m1","m2"]} -> new(1,2)
+                constructorArgs = new List<MemberBind>();
+                for (var i = 0; i < newExp.Arguments.Count; i++)
+                {
+                    var name = newExp.Members[i].Name;
+                    var valueExp = newExp.Arguments[i];
+                    var value = arg.convertService.ConvertToData(arg, valueExp);
+                    constructorArgs.Add(new MemberBind { name = name, value = value });
+                }
+            }
+            return constructorArgs;
+        }
+
+
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            ExpressionNode node = null;
+            if (expression is NewExpression newExp)
+            {
+                if (newExp.Type == typeof(DateTime) && arg.ReduceValue(newExp, out DateTime time))
+                {
+                    return ExpressionNode.Constant(value: time, type: typeof(DateTime));
+                }
+                List<MemberBind> constructorArgs = GetConstractorArgs(arg, newExp);
+                node = ExpressionNode.New(constructorArgs: constructorArgs, memberArgs: null);
+            }
+            else if (expression is MemberInitExpression memberInit)
+            {
+                // #3 constructor and memberInit    MemberInitExpression{Bindings=[{name:"m3",value:v3}],NewExpression} -> new(v1,v2){m3=v3}
+
+                // ##1 constructorArgs
+                List<MemberBind> constructorArgs = GetConstractorArgs(arg, memberInit.NewExpression);
+
+                // ##2 memberArgs
+                var memberArgs = new List<MemberBind>();
+                foreach (MemberBinding binding in memberInit.Bindings)
+                {
+                    if (binding is MemberAssignment assign)
+                    {
+                        var name = assign.Member.Name;
+                        var valueExp = assign.Expression;
+                        var value = arg.convertService.ConvertToData(arg, valueExp);
+                        memberArgs.Add(new MemberBind { name = name, value = value });
+                        continue;
+                    }
+                    throw new NotSupportedException($"Unsupported MemberInitExpression Binding: {binding.GetType()}");
+                }
+                node = ExpressionNode.New(constructorArgs: constructorArgs, memberArgs: memberArgs);
+            }
+
+            if (node != null)
+            {
+                node.New_SetType(expression.Type);
+            }
+            return node;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.nodeType != NodeType.New) return null;
+
+            ExpressionNode_New newNode = data;
+
+
+            // #1 constructorArgs
+            var constructorArgExpressions = newNode.constructorArgs
+                ?.Select(member => arg.convertService.ToExpression(arg, member.value)).ToArray();
+            constructorArgExpressions ??= new Expression[0];
+
+            Type type = arg?.getResultTypeForNewNode?.Invoke(newNode);
+            if (type == null)
+            {
+                type = newNode.New_GetType();
+            }
+            if (type == null && constructorArgExpressions?.Any() == true)
+            {
+                var properties = constructorArgExpressions
+                    .Select((item, i) => new { name = newNode.constructorArgs[i].name, Type = item.Type })
+                    .ToDictionary(member => member.name, member => member.Type);
+                type = ModelGenerator.CreateType(properties);
+            }
+
+            var constructor = type.GetConstructor(constructorArgExpressions?.Select(member => member.Type).ToArray() ?? Type.EmptyTypes);
+
+            var newExp = Expression.New(constructor, constructorArgExpressions);
+
+            if (newNode.memberArgs?.Any() != true) return newExp;
+
+            // #2 memberArgs
+            var memberArgs = newNode.memberArgs.Select(
+                 member => new { member.name, value = arg.convertService.ToExpression(arg, member.value) }
+             ).ToList();
+
+            var bindings = memberArgs.Select(member =>
+            {
+                var property = type.GetMember(member.name)?.FirstOrDefault();
+                //property ??= type.GetProperty(member.name);
+                return Expression.Bind(property, member.value);
+            }).ToArray();
+
+            return Expression.MemberInit(newExp, bindings);
+        }
+
+
+
+
+    }
+}

+ 80 - 0
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Unary.cs

@@ -0,0 +1,80 @@
+using System;
+using System.Collections;
+using System.Linq;
+using System.Linq.Expressions;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor
+{
+
+    public class Unary : IExpressionConvertor
+    {
+        public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
+        {
+            if (expression is UnaryExpression unary)
+            {
+                switch (unary.NodeType)
+                {
+                    case ExpressionType.Convert:
+                        return ExpressionNode.Convert(
+                            valueType: ComponentModel.ValueType.FromType(unary.Type),
+                            body: arg.convertService.ConvertToData(arg, unary.Operand)
+                            );
+                    case ExpressionType.Quote:
+                        return arg.convertService.ConvertToData(arg, unary.Operand);
+
+                    case ExpressionType.Not:
+                        return ExpressionNode.Not(body: arg.convertService.ConvertToData(arg, unary.Operand));
+
+                    default:
+                        return new ExpressionNode
+                        {
+                            nodeType = unary.NodeType.ToString(),
+                            expressionType = "Unary",
+                            body = arg.convertService.ConvertToData(arg, unary.Operand),
+                        };
+                }
+                throw new NotSupportedException($"Unsupported binary operator: {unary.NodeType}");
+            }
+
+            return null;
+        }
+
+        public Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data)
+        {
+            if (data.expressionType != "Unary") return null;
+
+            switch (data.nodeType)
+            {
+                case NodeType.Convert:
+                    {
+                        ExpressionNode_Convert convert = data;
+                        var value = arg.convertService.ToExpression(arg, convert.body);
+
+                        Type type = convert.valueType?.ToType();
+                        if (type == null) type = value?.Type;
+
+                        return Expression.Convert(value, type);
+                    }
+
+                //case NodeType.Not:
+                //    ExpressionNode_Not not = data;
+                //    return Expression.Not(arg.convertService.ToExpression(arg, not.body));
+
+                default:
+                    {
+                        var operand = arg.convertService.ToExpression(arg, data.body);
+
+                        var method = typeof(Expression).GetMethod(data.nodeType);
+                        return method?.Invoke(null, new object[] { operand }) as Expression;
+                    }
+            }
+            return null;
+        }
+
+
+
+
+    }
+}

+ 22 - 0
src/Vit.Linq/ExpressionTree/Extensions/Extensions/Queryable_Extensions_ExecuteDelete.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Vit.Extensions.Linq_Extensions
+{
+
+    public static partial class Queryable_Extensions
+    {
+        public static int ExecuteDelete(this IQueryable source)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+
+            return source.Provider.Execute<int>(
+                Expression.Call(
+                    null,
+                    new Func<IQueryable, int>(ExecuteDelete).Method
+                    , source.Expression));
+        }
+    }
+}

+ 23 - 0
src/Vit.Linq/ExpressionTree/Extensions/Extensions/Queryable_Extensions_ExecuteUpdate.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Vit.Extensions.Linq_Extensions
+{
+
+    public static partial class Queryable_Extensions
+    {
+        public static int ExecuteUpdate<Entity, EntityToUpdate>(this IQueryable<Entity> source, Expression<Func<Entity, EntityToUpdate>> update)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+
+            return source.Provider.Execute<int>(
+                Expression.Call(
+                    null,
+                    new Func<IQueryable<Entity>, Expression<Func<Entity, EntityToUpdate>>, int>(ExecuteUpdate).Method
+                    , source.Expression
+                    , update));
+        }
+    }
+}

+ 22 - 0
src/Vit.Linq/ExpressionTree/Extensions/Extensions/Queryable_Extensions_ToSql.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Vit.Extensions.Linq_Extensions
+{
+
+    public static partial class Queryable_Extensions
+    {
+        public static string ToSql(this IQueryable source)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+
+            return source.Provider.Execute<string>(
+                Expression.Call(
+                    null,
+                    new Func<IQueryable, string>(ToSql).Method
+                    , source.Expression));
+        }
+    }
+}

+ 27 - 0
src/Vit.Linq/ExpressionTree/Extensions/Queryable_Extensions_TotalCount.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Vit.Extensions.Linq_Extensions
+{
+
+    public static partial class Queryable_Extensions
+    {
+        public static int TotalCount(this IQueryable source)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+
+            return source.Provider.Execute<int>(
+                Expression.Call(
+                    null,
+                    new Func<IQueryable, int>(TotalCount).Method
+                    , source.Expression));
+        }
+
+        public static int TotalCount<T>(this IQueryable<T> source)
+        {
+            return TotalCount(source as IQueryable);
+        }
+    }
+}

+ 102 - 0
src/Vit.Linq/ExpressionTree/Extensions/Queryable_OrderBy_Extensions.cs

@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Extensions.Linq_Extensions
+{
+
+    public static partial class Queryable_OrderBy2_Extensions
+    {
+
+        public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, IEnumerable<SortField> orders, ExpressionConvertService Instance =null)
+        {
+            if (query == null || orders?.Any() != true) return query;
+
+            IOrderedQueryable<T> orderedQuery = null;
+            if (Instance == null) Instance = ExpressionConvertService.Instance;
+
+            foreach (var item in orders)
+            {
+                LambdaExpression exp = Instance.ToLambdaExpression(item.member, typeof(T));
+
+                if (orderedQuery == null)
+                {
+                    orderedQuery = OrderBy(query, exp, item.asc);
+                }
+                else
+                {
+                    orderedQuery = ThenBy(orderedQuery, exp, item.asc);
+                }
+            }
+            return orderedQuery;
+        }
+
+
+        #region OrderBy
+        static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, LambdaExpression exp, bool asc = true)
+        {
+            if (asc)
+            {
+                return (IOrderedQueryable<T>)MethodInfo_OrderBy(typeof(T), exp.ReturnType).Invoke(null, new object[] { query, exp });
+            }
+            else
+            {
+                return (IOrderedQueryable<T>)MethodInfo_OrderByDescending(typeof(T), exp.ReturnType).Invoke(null, new object[] { query, exp });
+            }
+        }
+
+        static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, LambdaExpression exp, bool asc = true)
+        {
+            if (asc)
+            {
+                return (IOrderedQueryable<T>)MethodInfo_ThenBy(typeof(T), exp.ReturnType).Invoke(null, new object[] { query, exp });
+            }
+            else
+            {
+                return (IOrderedQueryable<T>)MethodInfo_ThenByDescending(typeof(T), exp.ReturnType).Invoke(null, new object[] { query, exp });
+            }
+        }
+        #endregion
+
+
+        #region  CachedMethodInfo
+
+        private static   MethodInfo MethodInfo_OrderBy_;
+        static MethodInfo MethodInfo_OrderBy(Type sourceType, Type returnType) =>
+            (MethodInfo_OrderBy_ ??=
+                new Func<IQueryable<object>, Expression<Func<object, object>>, IOrderedQueryable<object>>(System.Linq.Queryable.OrderBy<object, object>)
+                .GetMethodInfo().GetGenericMethodDefinition())
+            .MakeGenericMethod(sourceType, returnType);
+
+
+        private static MethodInfo MethodInfo_OrderByDescending_;
+        static MethodInfo MethodInfo_OrderByDescending(Type sourceType, Type returnType) =>
+            (MethodInfo_OrderByDescending_ ??=
+                new Func<IQueryable<object>, Expression<Func<object, object>>, IOrderedQueryable<object>>(System.Linq.Queryable.OrderByDescending<object, object>)
+                .GetMethodInfo().GetGenericMethodDefinition())
+            .MakeGenericMethod(sourceType, returnType);
+
+
+        private static MethodInfo MethodInfo_ThenBy_;
+        static MethodInfo MethodInfo_ThenBy(Type sourceType, Type returnType) =>
+            (MethodInfo_ThenBy_ ??=
+                new Func<IOrderedQueryable<object>, Expression<Func<object, object>>, IOrderedQueryable<object>>(System.Linq.Queryable.ThenBy<object, object>)
+                .GetMethodInfo().GetGenericMethodDefinition())
+            .MakeGenericMethod(sourceType, returnType);
+
+        private static MethodInfo MethodInfo_ThenByDescending_;
+        static MethodInfo MethodInfo_ThenByDescending(Type sourceType, Type returnType) =>
+            (MethodInfo_ThenByDescending_ ??=
+                new Func<IOrderedQueryable<object>, Expression<Func<object, object>>, IOrderedQueryable<object>>(System.Linq.Queryable.ThenByDescending<object, object>)
+                .GetMethodInfo().GetGenericMethodDefinition())
+            .MakeGenericMethod(sourceType, returnType); 
+
+        #endregion
+
+    }
+}

+ 15 - 0
src/Vit.Linq/ExpressionTree/IExpressionConvertor.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Text;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Linq.ExpressionTree
+{
+    public interface IExpressionConvertor
+    {
+        ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression);
+        Expression ConvertToCode(CodeConvertArgument arg, ExpressionNode data);
+    }
+}

+ 6 - 6
src/Vit.Linq/QueryableBuilder.cs

@@ -11,8 +11,7 @@ namespace Vit.Linq
         public static IQueryable<Model> Build<Model>(Func<Expression, Type, object> QueryExecutor)
         {
             var queryProvider = new QueryProvider(QueryExecutor);
-            var query = new OrderedQueryable<Model>(queryProvider);
-            return query;
+            return new OrderedQueryable<Model>(queryProvider);
         }
 
 
@@ -70,8 +69,8 @@ namespace Vit.Linq
 
         public OrderedQueryable(QueryProvider provider)
         {
-            _expression = Expression.Constant(this);
             _provider = provider ?? throw new ArgumentNullException(nameof(provider));
+            _expression = Expression.Constant(this);
         }
 
         public OrderedQueryable(QueryProvider provider, Expression expression)
@@ -86,8 +85,9 @@ namespace Vit.Linq
                 throw new ArgumentOutOfRangeException(nameof(expression));
             }
 
-            _expression = expression;
             _provider = provider ?? throw new ArgumentNullException(nameof(provider));
+
+            _expression = expression;
         }
 
         public Type ElementType => typeof(T);
@@ -111,6 +111,7 @@ namespace Vit.Linq
     {
         Func<Expression, Type, object> QueryExecutor;
 
+
         public QueryProvider(Func<Expression, Type, object> QueryExecutor)
         {
             this.QueryExecutor = QueryExecutor;
@@ -139,8 +140,7 @@ namespace Vit.Linq
 
         public object ExecuteExpression(Expression expression, Type type)
         {
-            var data = QueryExecutor(expression, type);
-            return data;
+            return QueryExecutor(expression, type);
         }
     }
 

+ 6 - 2
src/Vit.Linq/Vit.Linq.csproj

@@ -7,12 +7,12 @@
     <PropertyGroup>
         <TargetFramework>netstandard2.0</TargetFramework>
         <Version>2.2.22-temp</Version>
-        <LangVersion>8.0</LangVersion>
+        <LangVersion>9.0</LangVersion>
     </PropertyGroup>
 
     <PropertyGroup>
         <Authors>Lith</Authors>
-        <Description>Linq Filter</Description>
+        <Description>Linq Extension</Description>
         <PackageProjectUrl>https://github.com/serset/Vit.Linq</PackageProjectUrl>
     </PropertyGroup>
 
@@ -20,5 +20,9 @@
         <DocumentationFile>bin\Debug\netstandard2.0\Vit.Linq.xml</DocumentationFile>
     </PropertyGroup>
 
+    <ItemGroup>
+        <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
+        <PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
+    </ItemGroup>
 
 </Project>

+ 40 - 0
src/Vit.Orm.Sqlite/Extensions/DbContext_Extensions.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Data;
+
+using Vit.Orm.Entity;
+using Vit.Orm.Entity.Dapper;
+using Vit.Orm.Sql;
+using Vit.Orm.Sqlite.Sql;
+
+namespace Vit.Orm.Sqlite.Extensions
+{
+    public static class DbContext_Extensions
+    {
+        public static DbContext UseSqlite(this DbContext dbContext, string ConnectionString)
+        {
+            ISqlTranslator sqlTranslator = new SqlTranslator(dbContext);
+
+            Func<IDbConnection> CreateDbConnection = () => new Microsoft.Data.Sqlite.SqliteConnection(ConnectionString);
+
+            Func<Type, IEntityDescriptor> GetEntityDescriptor = (type) => EntityDescriptor.GetEntityDescriptor(type);
+
+            Func<Type, IDbSet> DbSetCreator =
+                (type) => new SqlDbSetConstructor
+                {
+                    entityType = type,
+                    dbContext = dbContext,
+                    sqlTranslator = sqlTranslator,
+                    CreateDbConnection = CreateDbConnection,
+                    GetEntityDescriptor = GetEntityDescriptor
+                }
+                .CreateDbSet();
+
+            dbContext.DbSetCreator = DbSetCreator;
+
+            return dbContext;
+        }
+
+        
+
+    }
+}

+ 87 - 0
src/Vit.Orm.Sqlite/Sql/BatchUpdateTranslator.cs

@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.ComponentModel.CollectionsQuery;
+using Vit.Orm.Entity;
+
+namespace Vit.Orm.Sqlite.Sql
+{
+    public class BatchUpdateTranslator : QueryTranslator
+    {
+        /*
+--- single
+UPDATE `User` SET `name` = 'u'||id  where id > 0;
+
+-- 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
+)   -- select * from tmp;
+UPDATE `User`  
+  SET `name` =  ( SELECT `_name` FROM tmp WHERE tmp.id =`User`.id )
+where id in ( SELECT id FROM tmp );
+         */
+        public override string BuildQuery(JoinedStream 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 `{tableName}` ";
+
+            var sqlToUpdateCols = columnsToUpdate.Select(m => m.name).Select(name => $"{NewLine}  SET `{name}` =  ( SELECT `_{name}` FROM tmp WHERE tmp.`{keyName}` =`{tableName}`.`{keyName}` )");
+            sql += string.Join(",", sqlToUpdateCols);
+
+            sql += $"{NewLine}where `{keyName}` in ( SELECT `{keyName}` FROM tmp ); {NewLine}";
+
+            return sql;
+        }
+
+
+        List<MemberBind> columnsToUpdate;
+        IEntityDescriptor entityDescriptor;
+
+        public BatchUpdateTranslator(SqlTranslator sqlTranslator, Type entityType) : base(sqlTranslator, entityType)
+        {
+        }
+
+        protected override string ReadSelect(JoinedStream 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 entityToUpdate = fieldsToUpdate.New_GetType();
+            entityDescriptor = sqlTranslator.GetEntityDescriptor(entityToUpdate);
+
+
+            var sqlFields = new List<string>();
+
+            foreach (var column in columnsToUpdate)
+            {
+                sqlFields.Add($"({ReadValue(column.value)}) as `_{column.name}`");
+            }
+
+            // primary key
+            sqlFields.Add($"{sqlTranslator.GetSqlField(stream.left.alias, entityDescriptor.keyName)} as `{entityDescriptor.keyName}`");
+            return String.Join(",", sqlFields);
+        }
+
+
+
+    }
+}

+ 315 - 0
src/Vit.Orm.Sqlite/Sql/QueryTranslator.cs

@@ -0,0 +1,315 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Orm.DataReader;
+using Vit.Orm.Sql;
+using Vit.Orm.Sql.DataReader;
+using System.Linq.Expressions;
+using Vit.Linq.ExpressionTree.ComponentModel.CollectionsQuery;
+
+namespace Vit.Orm.Sqlite.Sql
+{
+    public class QueryTranslator
+    {
+        public SqlTranslator sqlTranslator { get; private set; }
+        public Type entityType { get; private set; }
+        public QueryTranslator(SqlTranslator sqlTranslator, Type entityType)
+        {
+            this.sqlTranslator = sqlTranslator;
+            this.entityType = entityType;
+        }
+
+
+
+        public IDbDataReader dataReader;
+        public Dictionary<string, object> sqlParam { get; private set; } = new Dictionary<string, object>();
+
+        int paramIndex = 0;
+        string NewParamName() => "param" + (paramIndex++);
+
+
+        protected virtual string ReadSelect(JoinedStream stream)
+        {
+            if (stream.method == "Count")
+            {
+                var reader = new NumScalarReader();
+                if (this.dataReader == null) this.dataReader = reader;
+                return "count(*)";
+            }
+            else if (string.IsNullOrWhiteSpace(stream.method) || stream.method == "ToList")
+            {
+                var reader = new EntityReader();
+
+                ExpressionNode selectedFields = stream.select?.fields as ExpressionNode;
+                if (selectedFields == null)
+                {
+                    selectedFields = ExpressionNode.Member(parameterName: stream.left.alias, memberName: null).Member_SetType(entityType);
+                }
+
+                var sqlFields = reader.BuildSelect(entityType, sqlTranslator, sqlTranslator.dbContext.convertService, selectedFields);
+                if (this.dataReader == null) this.dataReader = reader;
+                return sqlFields;
+            }
+            else
+            {
+                throw new NotSupportedException("not supported method: " + stream.method);
+            }
+        }
+
+
+        public virtual string BuildQuery(JoinedStream stream)
+        {
+            /* //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;
+             */
+
+            string sql = "";
+
+            // #0  select
+            sql += "select " + ReadSelect(stream);
+
+
+            #region #1 source
+            // from `User` u
+            sql += "\r\n from " + ReadInnerTable(stream.left);
+            #endregion
+
+            #region #2 join
+            stream.joins?.ForEach(joinedStream =>
+            {
+                sql += "\r\n " + (joinedStream.joinType == "leftJoin" ? "left join" : "inner join");
+                sql += " " + ReadInnerTable(joinedStream.right);
+
+                var where = ReadWhere(joinedStream.on);
+                if (!string.IsNullOrWhiteSpace(where)) sql += " on " + where;
+            });
+
+            #endregion
+
+            // #3 where 1=1
+            if (stream.where != null)
+            {
+                var where = ReadWhere(stream.where);
+                if (!string.IsNullOrWhiteSpace(where)) sql += "\r\n where " + where;
+            }
+
+            // #4 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);
+            }
+
+            // #5 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;
+        }
+
+        string ReadInnerTable(IStream stream)
+        {
+            if (stream is SourceStream sourceStream)
+            {
+                IQueryable query = sourceStream.GetSource() as IQueryable;
+                var tableName = sqlTranslator.GetTableName(query.ElementType);
+                return $"`{tableName}` as " + stream.alias;
+            }
+            if (stream is JoinedStream combinedStream)
+            {
+                var innerQuery = BuildQuery(combinedStream);
+                return $"({innerQuery}) as " + stream.alias;
+            }
+            throw new NotSupportedException();
+        }
+
+
+       protected string ReadValue(ExpressionNode data)
+       {
+            switch (data.nodeType)
+            {
+                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 ReadValue(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 ReadValue(convert.body);
+                        }
+
+                        if (targetDbType == "datetime")
+                        {
+                            return $"DATETIME({ReadValue(convert.body)})";
+                        }
+                        return $"cast({ReadValue(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("unsupported column type:" + type.Name);
+                        }
+                        #endregion
+                    }
+                case nameof(ExpressionType.Add):
+                    {
+                        ExpressionNode_Binary binary = data;
+
+                        // ##1 String Add
+                        if (data.valueType?.ToType() == typeof(string))
+                        {
+                            return $"{ReadValue(binary.left)} || {ReadValue(binary.right)}";
+                        }
+
+                        // ##2 Numberic Add
+                        return $"{ReadValue(binary.left)} + {ReadValue(binary.right)}";
+                    }
+                case nameof(ExpressionType.Coalesce):
+                    {
+                        ExpressionNode_Binary binary = data;
+                        return $"COALESCE({ReadValue(binary.left)},{ReadValue(binary.right)})";
+                    }
+                case NodeType.MethodCall:
+                    {
+                        ExpressionNode_MethodCall call = data;
+                        switch (call.methodName)
+                        {
+                            case "ToString":
+                                {
+                                    return $"cast({ReadValue(call.@object)} as text)";
+                                }
+                        }
+                        throw new NotSupportedException("unsupported method call: " + data.methodName);
+                    }
+            }
+            throw new NotSupportedException(data.nodeType);
+        }
+
+        string ReadWhere(ExpressionNode data)
+        {
+            switch (data.nodeType)
+            {
+                //case NodeType.Member:
+                case NodeType.Constant:
+                case NodeType.Convert:
+                    return ReadValue(data);
+
+                case NodeType.And:
+                    ExpressionNode_And and = data;
+                    return $"({ReadWhere(and.left)}) and ({ReadWhere(and.right)})";
+
+                case NodeType.Or:
+                    ExpressionNode_Or or = data;
+                    return $"({ReadWhere(or.left)}) or ({ReadWhere(or.right)})";
+
+                case NodeType.Not:
+                    ExpressionNode_Not not = data;
+                    return $"not ({ReadWhere(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 $"{ReadValue(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 $"{ReadValue(binary.right)} " + opera;
+                        }
+
+                        var @operator = operatorMap[data.nodeType];
+                        return $"{ReadValue(binary.left)} {@operator} {ReadValue(binary.right)}";
+                    }
+                case NodeType.LessThan:
+                case NodeType.LessThanOrEqual:
+                case NodeType.GreaterThan:
+                case NodeType.GreaterThanOrEqual:
+                    {
+                        ExpressionNode_Binary binary = data;
+                        var @operator = operatorMap[data.nodeType];
+                        return $"{ReadValue(binary.left)} {@operator} {ReadValue(binary.right)}";
+                    }
+                case NodeType.MethodCall:
+                    throw new NotSupportedException(data.nodeType);
+                    //ExpressionNode_MethodCall call = data;
+                    //return ConvertMethodToExpression(arg, call);
+            }
+
+            throw new NotSupportedException(data.nodeType);
+        }
+
+
+
+
+
+        readonly static Dictionary<string, string> operatorMap = new Dictionary<string, string>
+        {
+            [NodeType.Equal] = "=",
+            [NodeType.NotEqual] = "!=",
+            [NodeType.LessThan] = "<",
+            [NodeType.LessThanOrEqual] = "<=",
+            [NodeType.GreaterThan] = ">",
+            [NodeType.GreaterThanOrEqual] = ">=",
+        };
+
+    }
+
+}

+ 220 - 0
src/Vit.Orm.Sqlite/Sql/SqlTranslator.cs

@@ -0,0 +1,220 @@
+using System;
+using System.Collections.Generic;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.ComponentModel.CollectionsQuery;
+using Vit.Orm.Entity;
+using Vit.Orm.Sql;
+
+namespace Vit.Orm.Sqlite.Sql
+{
+    public class SqlTranslator : ISqlTranslator
+    {
+
+        public DbContext dbContext { get; private set; }
+
+        public SqlTranslator(DbContext dbContext)
+        {
+            this.dbContext = dbContext;
+        }
+
+
+        public string Create(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
+            if (entityDescriptor.columns != null)
+            {
+                foreach (var column in entityDescriptor.columns)
+                {
+                    sqlFields.Add(GetColumnSql(column));
+                }
+            }
+
+            return $@"
+CREATE TABLE `{entityDescriptor.tableName}` (
+{string.Join(",\r\n", sqlFields)}
+)";
+
+
+            #region GetColumnSql
+            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 $"  `{column.name}` {GetDbType(type)} {(nullable ? "DEFAULT NULL" : "NOT NULL")}";
+            }
+            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 (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) Query(JoinedStream joinedStream, Type entityType)
+        {
+            var query = new QueryTranslator(this, entityType: entityType);
+            string sql = query.BuildQuery(joinedStream);
+            return (sql, query.sqlParam, query.dataReader);
+        }
+
+        public (string sql, Dictionary<string, object> sqlParam) ExecuteUpdate(JoinedStream joinedStream, Type entityType)
+        {
+            var query = new BatchUpdateTranslator(this, entityType: entityType);
+            string sql = query.BuildQuery(joinedStream);
+            return (sql, query.sqlParam);
+        }
+
+        public (string sql, Dictionary<string, object> sqlParam) Insert<Entity>(DbSet<Entity> dbSet, Entity entity)
+        {
+            /* //sql
+             insert into user(name,birth,fatherId,motherId) values('','','');
+             select seq from sqlite_sequence where name='user';
+              */
+            var entityDescriptor = dbSet.entityDescriptor;
+            var sqlParam = new Dictionary<string, object>();
+
+            #region columns 
+            List<string> columnNames = new List<string>();
+            List<string> valueParams = new List<string>();
+            string columnName;
+            object value;
+
+            foreach (var column in entityDescriptor.allColumns)
+            {
+                columnName = column.name;
+                value = column.Get(entity);
+                //value ??= DBNull.Value;
+
+                columnNames.Add($"`{columnName}`");
+                valueParams.Add($"@{columnName}");
+                sqlParam[columnName] = value;
+            }
+            #endregion
+
+            // #2 build sql
+            string sql = $@"insert into `{entityDescriptor.tableName}`({string.Join(",", columnNames)}) values({string.Join(",", valueParams)});";
+            //sql+=$"select seq from sqlite_sequence where name = '{tableName}'; ";
+
+            return (sql, sqlParam);
+        }
+
+        public (string sql, Dictionary<string, object> sqlParam) Update<Entity>(DbSet<Entity> dbSet, Entity entity)
+        {
+            /* //sql
+                update user set name='' where id=7;
+            */
+
+            var entityDescriptor = dbSet.entityDescriptor;
+            var sqlParam = new Dictionary<string, object>();
+
+            // #1 columns
+            List<string> columnsToUpdate = new List<string>();
+            string columnName; object value;
+            foreach (var column in entityDescriptor.columns)
+            {
+                columnName = column.name;
+                value = column.Get(entity) ?? DBNull.Value;
+
+                columnsToUpdate.Add($"`{columnName}`=@{columnName}");
+                sqlParam[columnName] = value;
+            }
+
+
+            // #2 build sql
+            string sql = $@"update `{entityDescriptor.tableName}` set {string.Join(",", columnsToUpdate)} where `{entityDescriptor.keyName}`=@{entityDescriptor.keyName};";
+            sqlParam[entityDescriptor.keyName] = entityDescriptor.key.Get(entity);
+
+            return (sql, sqlParam);
+        }
+
+
+        public (string sql, Dictionary<string, object> sqlParam) Delete<Entity>(DbSet<Entity> dbSet, Entity entity)
+        {
+            /* //sql
+            delete from user where id=7;
+            */
+            var entityDescriptor = dbSet.entityDescriptor;
+            var sqlParam = new Dictionary<string, object>();
+
+            // #2 build sql
+            string sql = $@"delete from `{entityDescriptor.tableName}` where `{entityDescriptor.keyName}`=@{entityDescriptor.keyName};";
+            sqlParam[entityDescriptor.keyName] = entityDescriptor.key.Get(entity);
+
+            return (sql, sqlParam);
+        }
+        public (string sql, Dictionary<string, object> sqlParam) DeleteByKey<Entity>(DbSet<Entity> dbSet, object keyValue)
+        {
+            /* //sql
+            delete from user where id=7;
+            */
+            var entityDescriptor = dbSet.entityDescriptor;
+            var sqlParam = new Dictionary<string, object>();
+
+            // #2 build sql
+            string sql = $@"delete from `{entityDescriptor.tableName}` where `{entityDescriptor.keyName}`=@{entityDescriptor.keyName};";
+            sqlParam[entityDescriptor.keyName] = keyValue;
+
+            return (sql, sqlParam);
+        }
+
+        public string GetTableName(Type entityType)
+        {
+            return dbContext.GetEntityDescriptor(entityType)?.tableName;
+        }
+
+        public string GetSqlField(string tableName, string columnName)
+        {
+            return $"`{tableName}`.`{columnName}`";
+        }
+
+        public 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);
+        }
+
+        public IEntityDescriptor GetEntityDescriptor(Type entityType) => dbContext.GetEntityDescriptor(entityType);
+
+    }
+}

+ 17 - 0
src/Vit.Orm.Sqlite/Vit.Orm.Sqlite.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <pack>nuget</pack>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <TargetFramework>netstandard2.0</TargetFramework>
+        <Version>2.2.22-temp</Version>
+        <LangVersion>9.0</LangVersion>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Vit.Orm\Vit.Orm.csproj" />
+    </ItemGroup>
+
+</Project>

+ 40 - 0
src/Vit.Orm/DbContext.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree;
+using Vit.Orm.Entity;
+
+namespace Vit.Orm
+{
+    public class DbContext
+    {
+        public DbContext() { }
+
+        public virtual ExpressionConvertService convertService => ExpressionConvertService.Instance;
+
+        public Func<Type, IDbSet> DbSetCreator { set; protected get; }
+
+        ConcurrentDictionary<Type, IDbSet> dbSetMap = new();
+
+        public virtual IDbSet DbSet(Type entityType)
+        {
+            return dbSetMap.GetOrAdd(entityType, DbSetCreator);
+        }
+        public virtual DbSet<Entity> DbSet<Entity>()
+        {
+            return DbSet(typeof(Entity)) as DbSet<Entity>;
+        }
+
+
+        public virtual IEntityDescriptor GetEntityDescriptor(Type entityType) => DbSet(entityType)?.entityDescriptor;
+
+
+
+        public virtual Entity Insert<Entity>(Entity entity) => DbSet<Entity>().Insert(entity);
+        public virtual int Update<Entity>(Entity entity) => DbSet<Entity>().Update(entity);
+        public virtual int Delete<Entity>(Entity entity) => DbSet<Entity>().Delete(entity);
+        public virtual int DeleteByKey<Entity>(object keyValue) => DbSet<Entity>().DeleteByKey(keyValue);
+        public virtual IQueryable<Entity> Query<Entity>() => DbSet<Entity>().Query();
+    }
+}

+ 25 - 0
src/Vit.Orm/DbSet.cs

@@ -0,0 +1,25 @@
+using System.Linq;
+
+using Vit.Orm.Entity;
+
+namespace Vit.Orm
+{
+    public interface IDbSet
+    {
+        IEntityDescriptor entityDescriptor { get; }
+    }
+
+    public abstract class DbSet<Entity>: IDbSet
+    {
+        public abstract IEntityDescriptor entityDescriptor { get; }
+
+        public abstract void Create();
+        public abstract Entity Insert(Entity entity);
+        public abstract int Update(Entity entity);
+
+        public abstract int Delete(Entity entity);
+        public abstract int DeleteByKey(object keyValue);
+        public abstract IQueryable<Entity> Query();
+
+    }
+}

+ 31 - 0
src/Vit.Orm/Entity/ColumnDescriptor.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Reflection;
+
+namespace Vit.Orm.Entity
+{
+    public class ColumnDescriptor : IColumnDescriptor
+    {
+        public ColumnDescriptor(PropertyInfo propertyInfo, bool isPrimaryKey)
+        {
+            this.propertyInfo = propertyInfo;
+            this.isPrimaryKey = isPrimaryKey;
+        }
+
+        PropertyInfo propertyInfo;
+        public bool isPrimaryKey { get; private set; }
+        public string name => propertyInfo.Name;
+
+        public Type type => propertyInfo.PropertyType;
+
+        public void Set(object entity, object value)
+        {
+            propertyInfo.SetValue(entity, value);
+        }
+        public object Get(object entity)
+        {
+            return propertyInfo.GetValue(entity, null);
+        }
+    }
+
+
+}

+ 60 - 0
src/Vit.Orm/Entity/Dapper/EntityDescriptor.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Vit.Orm.Entity.Dapper
+{
+    public class EntityDescriptor: IEntityDescriptor
+    {
+        static ConcurrentDictionary<Type, EntityDescriptor> descMap = new();
+        static EntityDescriptor New(Type entityType) => new EntityDescriptor(entityType);
+        public static EntityDescriptor GetEntityDescriptor(Type entityType)
+        {
+            if (entityType?.GetCustomAttribute<global::Dapper.Contrib.Extensions.TableAttribute>() == null) return null;
+            //if (entityType == null) return null;
+            return descMap.GetOrAdd(entityType, New);
+        }
+
+        public static EntityDescriptor GetEntityDescriptor<Entity>()
+        {
+            return GetEntityDescriptor(typeof(Entity));
+        }
+
+        EntityDescriptor(Type entityType)
+        {
+            tableName = entityType.GetCustomAttribute<global::Dapper.Contrib.Extensions.TableAttribute>()?.Name;
+
+            var entityProperties = entityType?.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+            var keyProperty = entityProperties.FirstOrDefault(p => p.GetCustomAttribute<global::Dapper.Contrib.Extensions.KeyAttribute>() != null);
+            this.key = new ColumnDescriptor(keyProperty, true);
+
+            var properties = entityProperties.Where(p => p.GetCustomAttribute<global::Dapper.Contrib.Extensions.KeyAttribute>() == null);
+            this.columns = properties.Select(p => new ColumnDescriptor(p, false)).ToArray();
+        }
+
+        public string tableName { get; private set; }
+
+        /// <summary>
+        /// primary key name
+        /// </summary>
+        public string keyName => key?.name;
+
+        /// <summary>
+        /// primary key
+        /// </summary>
+        public IColumnDescriptor key { get; private set; }
+
+        /// <summary>
+        /// not include primary key
+        /// </summary>
+        public IColumnDescriptor[] columns { get; private set; }
+
+
+        public IEnumerable<IColumnDescriptor> allColumns => new[] { key }.Concat(columns);
+
+
+    }
+}

+ 13 - 0
src/Vit.Orm/Entity/IColumnDescriptor.cs

@@ -0,0 +1,13 @@
+using System;
+
+namespace Vit.Orm.Entity
+{
+    public interface IColumnDescriptor
+    {
+        bool isPrimaryKey { get; }
+        string name { get; }
+        Type type { get; }
+        void Set(object entity, object value);
+        object Get(object entity);
+    }
+}

+ 22 - 0
src/Vit.Orm/Entity/IEntityDescriptor.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+
+namespace Vit.Orm.Entity
+{
+    public interface IEntityDescriptor
+    {
+        string tableName { get; }
+        string keyName { get; }
+        /// <summary>
+        /// primary key
+        /// </summary>
+        public IColumnDescriptor key { get; }
+
+        /// <summary>
+        /// not include primary key
+        /// </summary>
+        public IColumnDescriptor[] columns { get; }
+
+        public IEnumerable<IColumnDescriptor> allColumns { get; }
+    }
+}

+ 104 - 0
src/Vit.Orm/README.md

@@ -0,0 +1,104 @@
+
+complex-query-operators https://learn.microsoft.com/en-us/ef/core/querying/complex-query-operators
+
+
+
+--------------
+# cur
+
+CREATE TABLE `user` (
+  `id` int NOT NULL ,
+  `name` varchar(100) DEFAULT NULL,
+  `birth` date DEFAULT NULL,
+  `fatherId` int DEFAULT NULL,
+  `motherId` int DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ;
+
+
+INSERT INTO `user` (id,name,birth,fatherId,motherId) VALUES
+	 (1,'u1','2021-01-01',5,6),
+	 (2,'u2','2021-01-02',5,6),
+	 (3,'u3','2021-01-03',5,6),
+	 (4,'u4','2021-01-04',NULL,NULL),
+	 (5,'uF','2021-01-05',NULL,NULL),
+	 (6,'uM','2021-01-06',NULL,NULL);
+
+
+
+
+                            case "Where__":
+                                {
+                                    var source = ReadStream(arg, call.arguments[0]);
+                                    var predicateLambda = call.arguments[1] as ExpressionNode_Lambda;
+                                    var where = ReadWhere(arg, source, predicateLambda);
+
+
+                                    JoinedStream joinedStream = source as JoinedStream;
+                                    if (joinedStream == null)
+                                    {
+                                        joinedStream = new JoinedStream(NewAliasName()) { left = source };
+                                    }
+
+                                    if (joinedStream.where == null)
+                                    {
+                                        joinedStream.where = where;
+                                    }
+                                    else
+                                    {
+                                        joinedStream.where = ExpressionNode.And(left: joinedStream.where, right: where);
+                                    }
+                                    return joinedStream;
+                                }
+
+
+
+## sql calc:      where a.name + '22' = 'lith22'
+## sql Func  
+## sql support case when
+
+
+
+
+--------------
+# TODO
+
+
+# distinct
+
+## ExecuteDelete
+
+## ExecuteUpdateToSql
+
+## ToArray  First| FirstOrDefault|...
+
+
+##   where (`t0`.`id` + 1 = 4) and (`t0`.`fatherId` = cast(5 as integer))
+
+
+
+
+
+--------------
+# done
+
+## ExecuteUpdate
+## sql calc:      set name = a.name + '22' 
+
+## Insert
+## Update
+## Delete
+
+## extensions for IQueryable.ToSql
+
+## remove nullable convert in sql
+## limit (Take Skip)
+## OrderBy
+## Count
+## select return null entity if not exist
+
+## left join
+
+## where
+ "= null"  ->   "is null" ,    "!=null" -> "is not null"   
+  is null , check by primary key

+ 108 - 0
src/Vit.Orm/Sql/DataReader/EntityReader.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Reflection;
+
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vit.Orm.Sql.DataReader
+{
+    public class EntityReader : IDbDataReader
+    {
+        public List<string> sqlFields { get; private set; } = new List<string>();
+
+        Type entityType;
+        List<IArgReader> entityArgReaders = new List<IArgReader>();
+        Delegate lambdaCreateEntity;
+
+
+        public string BuildSelect(Type entityType, ISqlTranslator sqlTranslator, ExpressionConvertService convertService, ExpressionNode selectedFields)
+        {
+            this.entityType = entityType;
+
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) =>
+            {
+                if (node?.nodeType == NodeType.Member)
+                {
+                    ExpressionNode_Member member = node;
+
+                    var argName = GetArgument(sqlTranslator, member);
+
+                    if (argName != null)
+                    {
+                        return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                    }
+                }
+                return default;
+            };
+            ExpressionNode_New newExp = cloner.Clone(selectedFields);
+
+
+            #region Compile Lambda
+            var lambdaNode = ExpressionNode.Lambda(entityArgReaders.Select(m => m.argName).ToArray(), (ExpressionNode)newExp);
+            //var strNode = Json.Serialize(lambdaNode);
+
+            var lambdaExp = convertService.ToLambdaExpression(lambdaNode, entityArgReaders.Select(m => m.argType).ToArray());
+
+            lambdaCreateEntity = lambdaExp.Compile();
+            #endregion
+
+            // sqlFields
+            return String.Join(",", sqlFields);
+        }
+
+
+        public object ReadData(IDataReader reader)
+        {
+            return new Func<IDataReader, List<string>>(ReadEntity<string>)
+              .GetMethodInfo().GetGenericMethodDefinition().MakeGenericMethod(entityType)
+              .Invoke(this, new object[] { reader });
+        }
+
+        List<Entity> ReadEntity<Entity>(IDataReader reader)
+        {
+            var list = new List<Entity>();
+
+            while (reader.Read())
+            {
+                var lambdaArgs = entityArgReaders.Select(m => m.Read(reader)).ToArray();
+                var obj = (Entity)lambdaCreateEntity.DynamicInvoke(lambdaArgs);
+                list.Add(obj);
+            }
+            return list;
+        }
+
+        string GetArgument(ISqlTranslator sqlTranslator, ExpressionNode_Member member)
+        {
+            // tableName_fieldName   tableName_
+            var argUniqueKey = $"arg_{member.objectValue?.parameterName ?? member.parameterName}_{member.memberName}";
+
+            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
+
+            if (argReader == null)
+            {
+                var argName = "arg_" + entityArgReaders.Count;
+                if (member.memberName != null)
+                {
+                    // Value arg
+                    string sqlFieldName = sqlTranslator.GetSqlField(member);
+                    argReader = new ValueReader(this, member, argUniqueKey, argName, sqlFieldName);
+                }
+                else
+                {
+                    // Entity arg
+                    var argType = member.Member_GetType();
+
+                    argReader = new ModelReader(this, sqlTranslator, member, argUniqueKey, argName, argType);
+                }
+                entityArgReaders.Add(argReader);
+            }
+            return argReader.argName;
+        }
+
+
+    }
+}

+ 14 - 0
src/Vit.Orm/Sql/DataReader/EntityReader/IArgReader.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Data;
+
+namespace Vit.Orm.Sql.DataReader
+{
+    interface IArgReader
+    {
+        string argUniqueKey { get; }
+        Type argType { get; }
+        string argName { get; }
+        object Read(IDataReader reader);
+    }
+
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов