Przeglądaj źródła

refactor DataReader

Lith 10 miesięcy temu
rodzic
commit
94ea1325c1
23 zmienionych plików z 422 dodań i 338 usunięć
  1. 32 6
      doc/TODO.md
  2. 176 0
      src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/EntityConstructor.cs
  3. 30 0
      src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/EntityPropertyReader.cs
  4. 1 1
      src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/IArgReader.cs
  5. 5 5
      src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/ModelReader.cs
  6. 1 1
      src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/ValueReader.cs
  7. 16 0
      src/Vitorm/Sql/DataReader/EntityConstructor/EntityConstructorConfig.cs
  8. 14 0
      src/Vitorm/Sql/DataReader/EntityConstructor/IEntityConstructor.cs
  9. 1 2
      src/Vitorm/Sql/DataReader/EntityConstructor/SqlFieldReader.cs
  10. 15 232
      src/Vitorm/Sql/DataReader/EntityReader.cs
  11. 0 35
      src/Vitorm/Sql/DataReader/EntityReader/ModelReader.EntityPropertyReader.cs
  12. 1 12
      src/Vitorm/Sql/DataReader/FirstEntityReader.cs
  13. 103 0
      src/Vitorm/Sql/DataReader/SqlColumns.cs
  14. 1 7
      src/Vitorm/Sql/SqlTranslate/BaseQueryTranslateService.cs
  15. 1 1
      src/Vitorm/StreamQuery/CombinedStream.cs
  16. 2 2
      src/Vitorm/StreamQuery/ResultSelector.cs
  17. 2 2
      src/Vitorm/StreamQuery/StreamReader.Join.cs
  18. 2 2
      src/Vitorm/StreamQuery/StreamReader.SelectMany.cs
  19. 8 8
      src/Vitorm/StreamQuery/StreamReader.cs
  20. 2 2
      test/Vitorm.Data.Benchmark/OrmRunner/BenchmarkRunner.cs
  21. 1 1
      test/Vitorm.Data.Benchmark/Program.cs
  22. 1 19
      test/Vitorm.Sqlite.MsTest/CommonTest/Query_LeftJoin_BySelectMany_Test.cs
  23. 7 0
      test/Vitorm.Sqlite.MsTest/CommonTest/Query_LinqMethods_Test.cs

+ 32 - 6
doc/TODO.md

@@ -1,11 +1,37 @@
 
 # TODO
 
- - support Async methods
- - support ClickHouse  
 
- - try to make it clean  
+# group then orderBy aggregate column
+> [QueryTranslator] not suported MethodCall: Sum
+``` csharp
+using var dbContext = DataSource.CreateDbContext();
+var userQuery = dbContext.Query<User>();
+{
+    var query =
+         userQuery
+        .GroupBy(user => new { user.fatherId, user.motherId })
+        .OrderBy(m => m.Sum(m => m.id))
+        .Select(userGroup => new
+        {
+            userGroup.Key.fatherId,
+            userGroup.Key.motherId,
+            sumId = userGroup.Sum(m => m.id),
+        });
 
- - DbContext.QueryProcedure<Entity>(arg)  
- - Save SaveRange  
- - DbFunction.PrimitiveSql  
+
+    var sql = query.ToExecuteString();
+    var rows = query.ToList();
+    var count = query.Count();
+}
+```
+
+
+
+# order by calculated column
+users.Select(user => new { user.id, fid = user.fatherId ?? -1 }).OrderBy(m => m.fid)
+
+
+
+# DbContext.QueryProcedure<Entity>(arg)  
+# DbFunction.PrimitiveSql  

+ 176 - 0
src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/EntityConstructor.cs

@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree;
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+using Vitorm.Sql.SqlTranslate;
+
+namespace Vitorm.Sql.DataReader.EntityConstructor.CompiledLambda
+{
+    public class EntityConstructor: IEntityConstructor
+    {
+        protected List<IArgReader> entityArgReaders = new List<IArgReader>();
+        protected Delegate lambdaCreateEntity;
+
+        public void Init(EntityConstructorConfig config, Type entityType, ExpressionNode resultSelector)
+        {
+            QueryTranslateArgument arg = config.arg;
+            ExpressionConvertService convertService = config.convertService;
+            ISqlTranslateService sqlTranslateService = config.sqlTranslateService;
+
+            var cloner = new ExpressionNodeCloner();
+            cloner.clone = (node) =>
+            {
+                if (node?.nodeType == NodeType.Member)
+                {
+                    ExpressionNode_Member member = node;
+
+                    var argName = GetArgument(config, member);
+
+                    if (argName != null)
+                    {
+                        return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                    }
+                }
+                else if (node?.nodeType == NodeType.MethodCall)
+                {
+                    ExpressionNode_MethodCall methodCall = node;
+
+                    // deal with aggregate functions like Sum(id)
+                    if (methodCall.methodCall_typeName == "Enumerable")
+                    {
+                        string argName = null;
+
+                        var sqlColumnSentence = sqlTranslateService.EvalExpression(arg, node);
+                        var columnType = methodCall.MethodCall_GetReturnType();
+                        argName = GetArgument(config, sqlColumnSentence, columnType);
+                        if (argName != null)
+                        {
+                            return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
+                        }
+                    }
+                }
+                return default;
+            };
+            ExpressionNode newResultSelector = cloner.Clone(resultSelector);
+
+            // compile ResultCreate lambda
+            lambdaCreateEntity = CompileExpression(convertService, entityArgReaders.Select(m => m.argName).ToArray(), newResultSelector);
+        }
+
+        public object ReadEntity(IDataReader reader)
+        {
+            var lambdaArgs = entityArgReaders.Select(m => m.Read(reader)).ToArray();
+            var entity = lambdaCreateEntity.DynamicInvoke(lambdaArgs);
+            return entity;
+        }
+
+
+
+        protected string GetArgument(EntityConstructorConfig config, ExpressionNode_Member member)
+        {
+
+            // 1: {"nodeType":"Member","parameterName":"a0","memberName":"id"}
+            // 2: {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
+            var tableName = member.objectValue?.parameterName ?? member.parameterName;
+
+
+            // tableName_fieldName   tableName_
+            var argUniqueKey = $"arg_{tableName}_{member.memberName}";
+
+            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
+
+            if (argReader == null)
+            {
+                var argName = "arg_" + entityArgReaders.Count;
+
+                var argType = member.Member_GetType();
+
+                bool isValueType = TypeUtil.IsValueType(argType);
+                if (isValueType)
+                {
+                    // Value arg
+                    var sqlColumnIndex = config.sqlColumns.AddSqlColumnAndGetIndex(config.sqlTranslateService, member, config.arg.dbContext);
+                    argReader = new ValueReader(argType, argUniqueKey, argName, sqlColumnIndex);
+                }
+                else
+                {
+                    // Entity arg
+                    var entityDescriptor = config.arg.dbContext.GetEntityDescriptor(argType);
+
+                    argReader = new ModelReader(config.sqlColumns, config.sqlTranslateService, tableName, argUniqueKey, argName, argType, entityDescriptor);
+                }
+                entityArgReaders.Add(argReader);
+            }
+            return argReader.argName;
+        }
+
+        protected string GetArgument(EntityConstructorConfig config, string sqlColumnSentence, Type columnType)
+        {
+            var argUniqueKey = $"argFunc_{sqlColumnSentence}";
+
+            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
+
+            if (argReader == null)
+            {
+                var argName = "arg_" + entityArgReaders.Count;
+
+                var sqlColumnIndex = config.sqlColumns.AddSqlColumnAndGetIndex(sqlColumnSentence);
+                argReader = new ValueReader(columnType, argUniqueKey, argName, sqlColumnIndex);
+
+                entityArgReaders.Add(argReader);
+            }
+            return argReader.argName;
+        }
+
+
+
+
+
+
+
+
+
+
+
+        Delegate CompileExpression(ExpressionConvertService convertService, string[] parameterNames, ExpressionNode newExp)
+        {
+            var lambdaNode = ExpressionNode.Lambda(entityArgReaders.Select(m => m.argName).ToArray(), newExp);
+            // var strNode = Json.Serialize(lambdaNode);
+
+            var lambdaExp = convertService.ToLambdaExpression(lambdaNode, entityArgReaders.Select(m => m.argType).ToArray());
+
+            return lambdaExp.Compile();
+        }
+
+        #region CompileExpressionWithCache
+        /*
+        // If it's anonymous CompilerGenerated type, reuse Compiled invoke.
+        // not work  because even if it's anonymous CompilerGenerated type, it also can be reused in same method of different lines.
+        static bool cacheEntityCompile = true;
+        static ConcurrentDictionary<Type, Delegate> delegateCache = new();
+        Delegate CompileExpressionWithCache(ExpressionConvertService convertService, string[] parameterNames, ExpressionNode_New newExp)
+        {
+            var type = entityType ?? newExp.New_GetType();
+
+            if (!cacheEntityCompile || type == null)
+                return CompileExpression(convertService, parameterNames, newExp);
+
+            var isCacheable = Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false);
+
+            if (isCacheable && delegateCache.TryGetValue(type, out var lambda)) return lambda;
+
+            lambda = CompileExpression(convertService, parameterNames, newExp);
+
+            if (isCacheable) delegateCache[type] = lambda;
+
+            return lambda;
+        }
+        //*/
+        #endregion
+
+    }
+}

+ 30 - 0
src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/EntityPropertyReader.cs

@@ -0,0 +1,30 @@
+using System.Data;
+
+using Vitorm.Entity;
+
+namespace Vitorm.Sql.DataReader.EntityConstructor.CompiledLambda
+{
+    class EntityPropertyReader : SqlFieldReader
+    {
+        public IColumnDescriptor column { get; protected set; }
+
+        public EntityPropertyReader(IColumnDescriptor column, int sqlColumnIndex) : base(column.type, sqlColumnIndex)
+        {
+            this.column = column;
+        }
+
+        public bool Read(IDataReader reader, object entity)
+        {
+            var value = Read(reader);
+            if (value != null)
+            {
+                column.SetValue(entity, value);
+                return true;
+            }
+
+            if (column.isKey) return false;
+            return true;
+        }
+    }
+
+}

+ 1 - 1
src/Vitorm/Sql/DataReader/EntityReader/IArgReader.cs → src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/IArgReader.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Data;
 
-namespace Vitorm.Sql.DataReader
+namespace Vitorm.Sql.DataReader.EntityConstructor.CompiledLambda
 {
     public interface IArgReader
     {

+ 5 - 5
src/Vitorm/Sql/DataReader/EntityReader/ModelReader.cs → src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/ModelReader.cs

@@ -3,11 +3,11 @@ using System.Collections.Generic;
 using System.Data;
 
 using Vitorm.Entity;
-using Vitorm.Sql.SqlTranslate;
+using Vitorm.Sql.SqlTranslate; 
 
-namespace Vitorm.Sql.DataReader
+namespace Vitorm.Sql.DataReader.EntityConstructor.CompiledLambda
 {
-    partial class ModelReader : IArgReader
+    class ModelReader : IArgReader
     {
         public string argName { get; set; }
         public string argUniqueKey { get; set; }
@@ -15,7 +15,7 @@ namespace Vitorm.Sql.DataReader
 
         readonly List<EntityPropertyReader> propertyReaders = new();
 
-        public ModelReader(EntityReader entityReader, ISqlTranslateService sqlTranslator, string tableName, string argUniqueKey, string argName, Type argType, IEntityDescriptor entityDescriptor)
+        public ModelReader(SqlColumns sqlColumns, ISqlTranslateService sqlTranslator, string tableName, string argUniqueKey, string argName, Type argType, IEntityDescriptor entityDescriptor)
         {
             this.argUniqueKey = argUniqueKey;
             this.argName = argName;
@@ -25,7 +25,7 @@ namespace Vitorm.Sql.DataReader
             {
                 foreach (var column in entityDescriptor.allColumns)
                 {
-                    var sqlColumnIndex = entityReader.sqlColumns.AddSqlColumnAndGetIndex(sqlTranslator, tableName, columnDescriptor: column);
+                    var sqlColumnIndex = sqlColumns.AddSqlColumnAndGetIndex(sqlTranslator, tableName, columnDescriptor: column);
                     propertyReaders.Add(new EntityPropertyReader(column, sqlColumnIndex));
                 }
             }

+ 1 - 1
src/Vitorm/Sql/DataReader/EntityReader/ValueReader.cs → src/Vitorm/Sql/DataReader/EntityConstructor/CompiledLambda/ValueReader.cs

@@ -1,6 +1,6 @@
 using System;
 
-namespace Vitorm.Sql.DataReader
+namespace Vitorm.Sql.DataReader.EntityConstructor.CompiledLambda
 {
 
     class ValueReader : SqlFieldReader, IArgReader

+ 16 - 0
src/Vitorm/Sql/DataReader/EntityConstructor/EntityConstructorConfig.cs

@@ -0,0 +1,16 @@
+using Vit.Linq.ExpressionTree;
+
+using Vitorm.Sql.SqlTranslate;
+
+namespace Vitorm.Sql.DataReader.EntityConstructor
+{
+    public class EntityConstructorConfig
+    {
+        public QueryTranslateArgument arg;
+
+        public ExpressionConvertService convertService;
+        public ISqlTranslateService sqlTranslateService;
+
+        public SqlColumns sqlColumns;
+    }
+}

+ 14 - 0
src/Vitorm/Sql/DataReader/EntityConstructor/IEntityConstructor.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Data;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+namespace Vitorm.Sql.DataReader.EntityConstructor
+{
+    public interface IEntityConstructor
+    {
+        void Init(EntityConstructorConfig config, Type entityType, ExpressionNode resultSelector);
+        object ReadEntity(IDataReader reader);
+
+    }
+}

+ 1 - 2
src/Vitorm/Sql/DataReader/EntityReader/SqlFieldReader.cs → src/Vitorm/Sql/DataReader/EntityConstructor/SqlFieldReader.cs

@@ -1,9 +1,8 @@
 using System;
 using System.Data;
 
-namespace Vitorm.Sql.DataReader
+namespace Vitorm.Sql.DataReader.EntityConstructor
 {
-
     class SqlFieldReader
     {
         public int sqlColumnIndex { get; set; }

+ 15 - 232
src/Vitorm/Sql/DataReader/EntityReader.cs

@@ -1,201 +1,41 @@
 using System;
 using System.Collections.Generic;
 using System.Data;
-using System.Linq;
 using System.Reflection;
 
 using Vit.Linq.ExpressionTree;
 using Vit.Linq.ExpressionTree.ComponentModel;
 
-using Vitorm.Entity;
+using Vitorm.Sql.DataReader.EntityConstructor;
 using Vitorm.Sql.SqlTranslate;
+using Vitorm.StreamQuery;
 
 namespace Vitorm.Sql.DataReader
 {
-    public class EntityReader : IDbDataReader
+    public partial class EntityReader : IDbDataReader
     {
+        public SqlColumns sqlColumns { get; } = new();
 
-        public class SqlColumns
-        {
-            List<Column> columns = new();
-
-            /// <summary>
-            /// entity field , try get sql column and return sqlColumnIndex
-            /// </summary>
-            /// <param name="sqlTranslator"></param>
-            /// <param name="tableName"></param>
-            /// <param name="columnDescriptor"></param>
-            /// <returns></returns>
-            public int AddSqlColumnAndGetIndex(ISqlTranslateService sqlTranslator, string tableName, IColumnDescriptor columnDescriptor)
-            {
-                var sqlColumnName = sqlTranslator.GetSqlField(tableName, columnDescriptor.columnName);
-
-                var sqlColumnIndex = columns.FirstOrDefault(m => m.sqlColumnName == sqlColumnName)?.sqlColumnIndex ?? -1;
-                if (sqlColumnIndex < 0)
-                {
-                    sqlColumnIndex = columns.Count;
-                    columns.Add(new Column { tableName = tableName, columnDescriptor = columnDescriptor, sqlColumnName = sqlColumnName, sqlColumnAlias = "c" + sqlColumnIndex, sqlColumnIndex = sqlColumnIndex });
-                }
-                return sqlColumnIndex;
-            }
-
-            /// <summary>
-            ///  aggregate column in GroupBy
-            /// </summary>
-            /// <param name="sqlColumnSentence"> for example:   Sum([t0].[userId])  ,  [t0].[userFatherId]  </param>
-            /// <returns></returns>
-            public int AddSqlColumnAndGetIndex(string sqlColumnSentence)
-            {
-                var sqlColumnName = sqlColumnSentence;
-
-                var sqlColumnIndex = columns.FirstOrDefault(m => m.sqlColumnName == sqlColumnName)?.sqlColumnIndex ?? -1;
-                if (sqlColumnIndex < 0)
-                {
-                    sqlColumnIndex = columns.Count;
-                    columns.Add(new Column { sqlColumnName = sqlColumnName, sqlColumnAlias = "c" + sqlColumnIndex, sqlColumnIndex = sqlColumnIndex });
-                }
-                return sqlColumnIndex;
-            }
-
-            /// <summary>
-            ///  alias table column  (  users.Select(u=> new { u.id } )   )
-            /// </summary>
-            /// <param name="sqlTranslator"></param>
-            /// <param name="member"></param>
-            /// <param name="dbContext"></param>
-            /// <returns></returns>
-            public int AddSqlColumnAndGetIndex(ISqlTranslateService sqlTranslator, ExpressionNode_Member member, DbContext dbContext)
-            {
-                var sqlColumnName = sqlTranslator.GetSqlField(member, dbContext);
-
-                var sqlColumnIndex = columns.FirstOrDefault(m => m.sqlColumnName == sqlColumnName)?.sqlColumnIndex ?? -1;
-                if (sqlColumnIndex < 0)
-                {
-                    sqlColumnIndex = columns.Count;
-                    columns.Add(new Column { member = member, sqlColumnName = sqlColumnName, sqlColumnAlias = "c" + sqlColumnIndex, sqlColumnIndex = sqlColumnIndex });
-                }
-                return sqlColumnIndex;
-            }
-
-
-            public string GetSqlColumns()
-            {
-                var sqlColumns = columns.Select(column => column.sqlColumnName + " as " + column.sqlColumnAlias);
-                return String.Join(", ", sqlColumns);
-            }
-
-            public string GetColumnAliasBySqlColumnName(string sqlColumnName)
-            {
-                return columns.FirstOrDefault(col => col.sqlColumnName == sqlColumnName)?.sqlColumnAlias;
-            }
-
-            class Column
-            {
-                // or table alias
-                public string tableName;
-                public IColumnDescriptor columnDescriptor;
-                public ExpressionNode_Member member;
-
-                public string sqlColumnName;
-                public string sqlColumnAlias;
-
-                public int sqlColumnIndex;
-            }
-
-        }
-
-        public SqlColumns sqlColumns = new();
-
-       
+        protected IEntityConstructor entityConstructor = new EntityConstructor.CompiledLambda.EntityConstructor();
 
 
         protected Type entityType;
-        protected List<IArgReader> entityArgReaders = new List<IArgReader>();
-        protected Delegate lambdaCreateEntity;
 
-        public string BuildSelect(QueryTranslateArgument arg, Type entityType, ISqlTranslateService sqlTranslateService, ExpressionConvertService convertService, ExpressionNode selectedFields)
+
+        public string BuildSelect(
+            QueryTranslateArgument arg, Type entityType, ISqlTranslateService sqlTranslateService, ExpressionConvertService convertService,
+            ResultSelector resultSelector, ExpressionNode selectedFields)
         {
             this.entityType = entityType;
 
-            var cloner = new ExpressionNodeCloner();
-            cloner.clone = (node) =>
-            {
-                if (node?.nodeType == NodeType.Member)
-                {
-                    ExpressionNode_Member member = node;
-
-                    var argName = GetArgument(arg, sqlTranslateService, member);
-
-                    if (argName != null)
-                    {
-                        return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
-                    }
-                }
-                else if (node?.nodeType == NodeType.MethodCall)
-                {
-                    ExpressionNode_MethodCall methodCall = node;
+            var config = new EntityConstructorConfig { arg = arg, convertService = convertService, sqlTranslateService = sqlTranslateService, sqlColumns = sqlColumns };
 
-                    // deal with aggregate functions like Sum(id)
-                    if (methodCall.methodCall_typeName == "Enumerable")
-                    {
-                        string argName = null;
-
-                        var sqlColumnSentence = sqlTranslateService.EvalExpression(arg, node);
-                        var columnType = methodCall.MethodCall_GetReturnType();
-                        argName = GetArgument(sqlColumnSentence, columnType);
-                        if (argName != null)
-                        {
-                            return (true, ExpressionNode.Member(parameterName: argName, memberName: null));
-                        }
-                    }
-                }
-                return default;
-            };
-            ExpressionNode newSelectedFields = cloner.Clone(selectedFields);
-
-            // Compile Lambda
-            lambdaCreateEntity = CompileExpression(convertService, entityArgReaders.Select(m => m.argName).ToArray(), newSelectedFields);
+            entityConstructor.Init(config, entityType, selectedFields);
 
             return sqlColumns.GetSqlColumns();
         }
 
-
-        Delegate CompileExpression(ExpressionConvertService convertService, string[] parameterNames, ExpressionNode newExp)
-        {
-            var lambdaNode = ExpressionNode.Lambda(entityArgReaders.Select(m => m.argName).ToArray(), newExp);
-            // var strNode = Json.Serialize(lambdaNode);
-
-            var lambdaExp = convertService.ToLambdaExpression(lambdaNode, entityArgReaders.Select(m => m.argType).ToArray());
-
-            return lambdaExp.Compile();
-        }
-
-        #region CompileExpressionWithCache
-        /*
-        // If it's anonymous CompilerGenerated type, reuse Compiled invoke.
-        // not work  because even if it's anonymous CompilerGenerated type, it also can be reused in same method of different lines.
-        static bool cacheEntityCompile = true;
-        static ConcurrentDictionary<Type, Delegate> delegateCache = new();
-        Delegate CompileExpressionWithCache(ExpressionConvertService convertService, string[] parameterNames, ExpressionNode_New newExp)
-        {
-            var type = entityType ?? newExp.New_GetType();
-
-            if (!cacheEntityCompile || type == null)
-                return CompileExpression(convertService, parameterNames, newExp);
-
-            var isCacheable = Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false);
-
-            if (isCacheable && delegateCache.TryGetValue(type, out var lambda)) return lambda;
-
-            lambda = CompileExpression(convertService, parameterNames, newExp);
-
-            if (isCacheable) delegateCache[type] = lambda;
-
-            return lambda;
-        }
-        //*/
-        #endregion
-
+    
 
         public virtual object ReadData(IDataReader reader)
         {
@@ -204,76 +44,19 @@ namespace Vitorm.Sql.DataReader
               .Invoke(this, new object[] { reader });
         }
 
+
         object 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);
+                var row = (Entity)entityConstructor.ReadEntity(reader);
+                list.Add(row);
             }
-
             return list;
         }
 
-        protected string GetArgument(QueryTranslateArgument arg, ISqlTranslateService sqlTranslator, ExpressionNode_Member member)
-        {
-
-            // 1: {"nodeType":"Member","parameterName":"a0","memberName":"id"}
-            // 2: {"nodeType":"Member","objectValue":{"parameterName":"a0","nodeType":"Member"},"memberName":"id"}
-            var tableName = member.objectValue?.parameterName ?? member.parameterName;
-
-
-            // tableName_fieldName   tableName_
-            var argUniqueKey = $"arg_{tableName}_{member.memberName}";
-
-            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
-
-            if (argReader == null)
-            {
-                var argName = "arg_" + entityArgReaders.Count;
-
-                var argType = member.Member_GetType();
-
-                bool isValueType = TypeUtil.IsValueType(argType);
-                if (isValueType)
-                {
-                    // Value arg
-                    var sqlColumnIndex = sqlColumns.AddSqlColumnAndGetIndex(sqlTranslator, member, arg.dbContext);
-                    argReader = new ValueReader(argType, argUniqueKey, argName, sqlColumnIndex);
-                }
-                else
-                {
-                    // Entity arg
-                    var entityDescriptor = arg.dbContext.GetEntityDescriptor(argType);
-
-                    argReader = new ModelReader(this, sqlTranslator, tableName, argUniqueKey, argName, argType, entityDescriptor);
-                }
-                entityArgReaders.Add(argReader);
-            }
-            return argReader.argName;
-        }
-
-        protected string GetArgument(string sqlColumnSentence, Type columnType)
-        {
-            var argUniqueKey = $"argFunc_{sqlColumnSentence}";
-
-            IArgReader argReader = entityArgReaders.FirstOrDefault(reader => reader.argUniqueKey == argUniqueKey);
-
-            if (argReader == null)
-            {
-                var argName = "arg_" + entityArgReaders.Count;
-
-                var sqlColumnIndex = sqlColumns.AddSqlColumnAndGetIndex(sqlColumnSentence);
-                argReader = new ValueReader(columnType, argUniqueKey, argName, sqlColumnIndex);
-
-                entityArgReaders.Add(argReader);
-            }
-            return argReader.argName;
-        }
-
 
 
     }

+ 0 - 35
src/Vitorm/Sql/DataReader/EntityReader/ModelReader.EntityPropertyReader.cs

@@ -1,35 +0,0 @@
-using System.Data;
-
-using Vitorm.Entity;
-
-namespace Vitorm.Sql.DataReader
-{
-    partial class ModelReader
-    {
-        class EntityPropertyReader : SqlFieldReader
-        {
-            public IColumnDescriptor column { get; protected set; }
-
-            public EntityPropertyReader(IColumnDescriptor column, int sqlColumnIndex)
-                : base(column.type, sqlColumnIndex)
-            {
-                this.column = column;
-            }
-            public bool Read(IDataReader reader, object entity)
-            {
-                var value = Read(reader);
-                if (value != null)
-                {
-                    column.SetValue(entity, value);
-                    return true;
-                }
-
-                if (column.isKey) return false;
-                return true;
-            }
-        }
-    }
-
-
-
-}

+ 1 - 12
src/Vitorm/Sql/DataReader/FirstEntityReader.cs

@@ -1,7 +1,5 @@
 using System;
 using System.Data;
-using System.Linq;
-using System.Reflection;
 
 namespace Vitorm.Sql.DataReader
 {
@@ -9,19 +7,10 @@ namespace Vitorm.Sql.DataReader
     {
         public bool nullable = true;
         public override object ReadData(IDataReader reader)
-        {
-            return new Func<IDataReader, string>(ReadEntity<string>)
-              .GetMethodInfo().GetGenericMethodDefinition().MakeGenericMethod(entityType)
-              .Invoke(this, new object[] { reader });
-        }
-
-        Entity ReadEntity<Entity>(IDataReader reader)
         {
             if (reader.Read())
             {
-                var lambdaArgs = entityArgReaders.Select(m => m.Read(reader)).ToArray();
-                var obj = (Entity)lambdaCreateEntity.DynamicInvoke(lambdaArgs);
-                return obj;
+                return entityConstructor.ReadEntity(reader);
             }
             if (!nullable) throw new InvalidOperationException("Sequence contains no elements");
             return default;

+ 103 - 0
src/Vitorm/Sql/DataReader/SqlColumns.cs

@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Vit.Linq.ExpressionTree.ComponentModel;
+
+using Vitorm.Entity;
+using Vitorm.Sql.SqlTranslate;
+
+namespace Vitorm.Sql.DataReader
+{
+    public class SqlColumns
+    {
+        List<Column> columns = new();
+
+        /// <summary>
+        /// entity field , try get sql column and return sqlColumnIndex
+        /// </summary>
+        /// <param name="sqlTranslator"></param>
+        /// <param name="tableName"></param>
+        /// <param name="columnDescriptor"></param>
+        /// <returns></returns>
+        public int AddSqlColumnAndGetIndex(ISqlTranslateService sqlTranslator, string tableName, IColumnDescriptor columnDescriptor)
+        {
+            var sqlColumnName = sqlTranslator.GetSqlField(tableName, columnDescriptor.columnName);
+
+            var sqlColumnIndex = columns.FirstOrDefault(m => m.sqlColumnName == sqlColumnName)?.sqlColumnIndex ?? -1;
+            if (sqlColumnIndex < 0)
+            {
+                sqlColumnIndex = columns.Count;
+                columns.Add(new Column { tableName = tableName, columnDescriptor = columnDescriptor, sqlColumnName = sqlColumnName, sqlColumnAlias = "c" + sqlColumnIndex, sqlColumnIndex = sqlColumnIndex });
+            }
+            return sqlColumnIndex;
+        }
+
+        /// <summary>
+        ///  aggregate column in GroupBy
+        /// </summary>
+        /// <param name="sqlColumnSentence"> for example:   Sum([t0].[userId])  ,  [t0].[userFatherId]  </param>
+        /// <returns></returns>
+        public int AddSqlColumnAndGetIndex(string sqlColumnSentence)
+        {
+            var sqlColumnName = sqlColumnSentence;
+
+            var sqlColumnIndex = columns.FirstOrDefault(m => m.sqlColumnName == sqlColumnName)?.sqlColumnIndex ?? -1;
+            if (sqlColumnIndex < 0)
+            {
+                sqlColumnIndex = columns.Count;
+                columns.Add(new Column { sqlColumnName = sqlColumnName, sqlColumnAlias = "c" + sqlColumnIndex, sqlColumnIndex = sqlColumnIndex });
+            }
+            return sqlColumnIndex;
+        }
+
+        /// <summary>
+        ///  alias table column  (  users.Select(u=> new { u.id } )   )
+        /// </summary>
+        /// <param name="sqlTranslator"></param>
+        /// <param name="member"></param>
+        /// <param name="dbContext"></param>
+        /// <returns></returns>
+        public int AddSqlColumnAndGetIndex(ISqlTranslateService sqlTranslator, ExpressionNode_Member member, DbContext dbContext)
+        {
+            var sqlColumnName = sqlTranslator.GetSqlField(member, dbContext);
+
+            var sqlColumnIndex = columns.FirstOrDefault(m => m.sqlColumnName == sqlColumnName)?.sqlColumnIndex ?? -1;
+            if (sqlColumnIndex < 0)
+            {
+                sqlColumnIndex = columns.Count;
+                columns.Add(new Column { member = member, sqlColumnName = sqlColumnName, sqlColumnAlias = "c" + sqlColumnIndex, sqlColumnIndex = sqlColumnIndex });
+            }
+            return sqlColumnIndex;
+        }
+
+
+        public string GetSqlColumns()
+        {
+            var sqlColumns = columns.Select(column => column.sqlColumnName + " as " + column.sqlColumnAlias);
+            return String.Join(", ", sqlColumns);
+        }
+
+        public string GetColumnAliasBySqlColumnName(string sqlColumnName)
+        {
+            return columns.FirstOrDefault(col => col.sqlColumnName == sqlColumnName)?.sqlColumnAlias;
+        }
+
+        class Column
+        {
+            // or table alias
+            public string tableName;
+            public IColumnDescriptor columnDescriptor;
+            public ExpressionNode_Member member;
+
+            public string sqlColumnName;
+            public string sqlColumnAlias;
+
+            public int sqlColumnIndex;
+        }
+
+    }
+
+
+
+}

+ 1 - 7
src/Vitorm/Sql/SqlTranslate/BaseQueryTranslateService.cs

@@ -181,15 +181,9 @@ namespace Vitorm.Sql.SqlTranslate
             //if (resultEntityType == null)
             //    throw new NotSupportedException("resultEntityType could not be null");
 
-            if (stream.method == "Count")
-            {
-                var sqlColumns = reader.BuildSelect(arg, resultEntityType, sqlTranslator, arg.dbContext.convertService, selectedFields);
-                arg.dataReader ??= reader;
-                return (stream.distinct == true ? "distinct " : "") + sqlColumns;
-            }
 
             {
-                var sqlColumns = reader.BuildSelect(arg, resultEntityType, sqlTranslator, arg.dbContext.convertService, selectedFields);
+                var sqlColumns = reader.BuildSelect(arg, resultEntityType, sqlTranslator, arg.dbContext.convertService, stream.select, selectedFields);
                 arg.dataReader ??= reader;
                 return (stream.distinct == true ? "distinct " : "") + sqlColumns;
             }

+ 1 - 1
src/Vitorm/StreamQuery/CombinedStream.cs

@@ -48,7 +48,7 @@ value(Vit.Linq.Converter.OrderedQueryable`1[Vit.Linq.MsTest.Converter.Join_Test+
         public string alias { get; protected set; }
 
         // ExpressionNode_New   new { c = a , d = b }
-        public SelectedFields select { get; set; }
+        public ResultSelector select { get; set; }
         public bool? distinct;
 
 

+ 2 - 2
src/Vitorm/StreamQuery/SelectedFields.cs → src/Vitorm/StreamQuery/ResultSelector.cs

@@ -2,9 +2,9 @@
 
 namespace Vitorm.StreamQuery
 {
-    public class SelectedFields
+    public class ResultSelector
     {
-        // root value of ExpressionNode_Member is IStream
+        public ExpressionNode_Lambda resultSelector { get; set; }
         public ExpressionNode fields { get; set; }
 
         public bool? isDefaultSelect { get; set; }

+ 2 - 2
src/Vitorm/StreamQuery/StreamReader.Join.cs

@@ -99,7 +99,7 @@ namespace Vitorm.StreamQuery
 
 
             // #4 read SelectedFields
-            SelectedFields select;
+            ResultSelector select;
             {
                 // left
                 var parameterName = resultSelector.parameterNames[0];
@@ -111,7 +111,7 @@ namespace Vitorm.StreamQuery
                 parameterValue = ExpressionNode_RenameableMember.Member(stream: rightStreamToJoin.right, resultSelector.Lambda_GetParamTypes()[1]);
                 argForSelect = argForSelect.SetParameter(parameterName, parameterValue);
 
-                select = ReadFieldSelect(argForSelect, resultSelector);
+                select = ReadResultSelector(argForSelect, resultSelector);
             }
 
 

+ 2 - 2
src/Vitorm/StreamQuery/StreamReader.SelectMany.cs

@@ -132,7 +132,7 @@ namespace Vitorm.StreamQuery
 
 
             // #3 read SelectedFields
-            SelectedFields select;
+            ResultSelector select;
             {
                 // left
                 var parameterName = resultSelector.parameterNames[0];
@@ -144,7 +144,7 @@ namespace Vitorm.StreamQuery
                 parameterValue = ExpressionNode_RenameableMember.Member(stream: rightStreamToJoin.right, resultSelector.Lambda_GetParamTypes()[1]);
                 argForSelect = argForSelect.SetParameter(parameterName, parameterValue);
 
-                select = ReadFieldSelect(argForSelect, resultSelector);
+                select = ReadResultSelector(argForSelect, resultSelector);
             }
 
             // #4 combine stream

+ 8 - 8
src/Vitorm/StreamQuery/StreamReader.cs

@@ -197,7 +197,7 @@ namespace Vitorm.StreamQuery
         {
             Type entityType = source.GetEntityType();
             var selectedFields = ExpressionNode.Member(parameterName: source.alias, memberName: null).Member_SetType(entityType);
-            var select = new SelectedFields { fields = selectedFields, isDefaultSelect = true };
+            var select = new ResultSelector { fields = selectedFields, isDefaultSelect = true };
 
             return new CombinedStream(NewAliasName()) { source = source, select = select };
         }
@@ -268,7 +268,7 @@ namespace Vitorm.StreamQuery
                                                 var parameterName = resultSelector.parameterNames[0];
                                                 var parameterValue = ExpressionNode_RenameableMember.Member(stream: sourceStream, resultSelector.Lambda_GetParamTypes()[0]);
                                                 var newArg = arg.WithParameter(parameterName, parameterValue);
-                                                var select = ReadFieldSelect(newArg, resultSelector);
+                                                var select = ReadResultSelector(newArg, resultSelector);
 
                                                 return new CombinedStream(NewAliasName()) { source = sourceStream, select = select };
                                             }
@@ -281,7 +281,7 @@ namespace Vitorm.StreamQuery
                                                 var noChildParameterValue = ExpressionNode_RenameableMember.Member(stream: groupedStream.source, resultSelector.Lambda_GetParamTypes()[0]);
                                                 var newArg = arg.WithParameter(parameterName, parameterValue, noChildParameterValue: noChildParameterValue);
 
-                                                var select = ReadFieldSelect(newArg, resultSelector);
+                                                var select = ReadResultSelector(newArg, resultSelector);
                                                 groupedStream.select = select;
                                                 return groupedStream;
                                             }
@@ -290,7 +290,7 @@ namespace Vitorm.StreamQuery
                                                 var parameterName = resultSelector.parameterNames[0];
                                                 var parameterValue = combinedStream.select.fields;
                                                 var newArg = arg.WithParameter(parameterName, parameterValue);
-                                                var select = ReadFieldSelect(newArg, resultSelector);
+                                                var select = ReadResultSelector(newArg, resultSelector);
 
                                                 combinedStream.select = select;
                                                 return combinedStream;
@@ -308,14 +308,14 @@ namespace Vitorm.StreamQuery
                                                 var parameterName = resultSelector.parameterNames[0];
                                                 var parameterValue = ExpressionNode_RenameableMember.Member(stream: sourceStream, resultSelector.Lambda_GetParamTypes()[0]);
 
-                                                var select = ReadFieldSelect(arg.WithParameter(parameterName, parameterValue), resultSelector);
+                                                var select = ReadResultSelector(arg.WithParameter(parameterName, parameterValue), resultSelector);
                                                 return new StreamToUpdate(sourceStream) { fieldsToUpdate = select.fields };
                                             }
                                         case CombinedStream combinedStream:
                                             {
                                                 var parameterName = resultSelector.parameterNames[0];
                                                 var parameterValue = combinedStream.select.fields;
-                                                var select = ReadFieldSelect(arg.WithParameter(parameterName, parameterValue), resultSelector);
+                                                var select = ReadResultSelector(arg.WithParameter(parameterName, parameterValue), resultSelector);
 
                                                 return new StreamToUpdate(source) { fieldsToUpdate = select.fields };
                                             }
@@ -425,7 +425,7 @@ namespace Vitorm.StreamQuery
             var fields = arg.DeepClone(node);
             return fields;
         }
-        SelectedFields ReadFieldSelect(Argument arg, ExpressionNode_Lambda resultSelector)
+        ResultSelector ReadResultSelector(Argument arg, ExpressionNode_Lambda resultSelector)
         {
             ExpressionNode node = resultSelector.body;
             if (node?.nodeType != NodeType.New && node?.nodeType != NodeType.Member && node?.nodeType != NodeType.Convert)
@@ -452,7 +452,7 @@ namespace Vitorm.StreamQuery
                 isDefaultSelect = !(existCalculatedField ?? false);
             }
 
-            return new() { fields = fields, isDefaultSelect = isDefaultSelect };
+            return new() { fields = fields, isDefaultSelect = isDefaultSelect, resultSelector = resultSelector };
         }
 
         ExpressionNode ReadSortField(ExpressionNode_Lambda resultSelector, CombinedStream stream)

+ 2 - 2
test/Vitorm.Data.Benchmark/OrmRunner/BenchmarkRunner.cs

@@ -27,7 +27,7 @@ namespace App.OrmRunner
         [Params(false, true)]
         public bool queryJoin = false;
 
-        //[Params(0, 10)]
+        [Params(0, 10)]
         public int? skip = 10;
 
         [Params(10, 1000)]
@@ -35,7 +35,7 @@ namespace App.OrmRunner
 
 
 
-        [Params(typeof(Runner_Vitorm), typeof(Runner_EntityFramework), typeof(Runner_SqlSuger))]
+        [Params(typeof(Runner_Vitorm), typeof(Runner_EntityFramework) )]
         public Type runner;
 
 

+ 1 - 1
test/Vitorm.Data.Benchmark/Program.cs

@@ -9,7 +9,7 @@ namespace App
         static void Main(string[] args)
         {
             // #1 init
-            App.Runner.EnvSetup.InitDb();
+            //App.Runner.EnvSetup.InitDb();
 
 
 

+ 1 - 19
test/Vitorm.Sqlite.MsTest/CommonTest/Query_LeftJoin_BySelectMany_Test.cs

@@ -16,25 +16,7 @@ namespace Vitorm.MsTest.CommonTest
         {
             using var dbContext = DataSource.CreateDbContext();
             var userQuery = dbContext.Query<User>();
-
-            // Linq Expression
-            {
-                var query =
-                        from user in userQuery
-                        from father in userQuery.Where(father => user.fatherId == father.id).DefaultIfEmpty()
-                        where user.id > 2
-                        orderby user.id
-                        select new { user, father };
-
-                var sql = query.ToExecuteString();
-                var userList = query.ToList();
-
-                Assert.AreEqual(4, userList.Count);
-                Assert.AreEqual(3, userList[0].user.id);
-                Assert.AreEqual(5, userList[0].father?.id);
-                Assert.AreEqual(4, userList[1].user.id);
-                Assert.AreEqual(null, userList[1].father?.name);
-            }
+ 
 
             // Lambda Expression
             {

+ 7 - 0
test/Vitorm.Sqlite.MsTest/CommonTest/Query_LinqMethods_Test.cs

@@ -105,6 +105,13 @@ namespace Vitorm.MsTest.CommonTest
             }
 
 
+
+            {
+                userQuery.Where(user => user.id == 3).Select(u => new { u.id, u.fatherId }).ToList();
+
+            }
+
+
             {
                 var query =
                     from user in userQuery