Lith há 11 meses atrás
pai
commit
b612210102

+ 58 - 0
src/TODO/Enumerable_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;
+using System.Collections.Generic;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Enumerable_Methods
+{
+
+    /// <summary>
+    ///  Queryable.Any
+    /// </summary>
+    public class Any : MethodConvertor_Common
+    {
+        // public static bool Any<TSource>(this IEnumerable<TSource> source);
+        // public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+        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.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<IEnumerable<string>, bool>(Enumerable.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<IEnumerable<string>, Func<string, bool>, bool>(Enumerable.Any<string>))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+                return Expression.Call(method, methodArguments);
+            }
+
+            throw new NotSupportedException($"Method not supported: {call.methodName}");
+        }
+    }
+
+
+
+}

+ 46 - 0
src/TODO/Enumerable_Methods/Contains.cs

@@ -0,0 +1,46 @@
+using System;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Extensions.Linq_Extensions;
+using System.Collections.Generic;
+
+namespace Vit.Linq.ExpressionTree.ExpressionConvertor.MethodCalls.Enumerable_Methods
+{
+
+    /// <summary>
+    ///  Enumerable.Contains
+    /// </summary>
+    public class Contains : MethodConvertor_Common
+    {
+        // public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value);
+        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.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 IEnumerable<TSource> source)
+            if (call.arguments?.Length == 1)
+            {
+                var value = arg.convertService.ToExpression(arg, call.arguments[1]);
+
+
+                var methodArguments = new[] { expSource, value };
+
+                var method = (new Func<IEnumerable<string>, bool>(Enumerable.Any<string>))
+                                .Method.GetGenericMethodDefinition().MakeGenericMethod(elementType);
+                return Expression.Call(method, methodArguments);
+            }
+
+            throw new NotSupportedException($"Method not supported: {call.methodName}");
+        }
+    }
+
+
+
+}

+ 12 - 12
src/Test/Vit.Linq.ExpressionTree.MsTest/QueryAction_Test.cs

@@ -32,7 +32,8 @@ namespace Vit.Linq.ExpressionTree.MsTest
                 {
                     // #1 Code to Data
                     // query => query.Where().OrderBy().Skip().Take().Select().ToList();
-                    node = convertService.ConvertToData(expression, autoReduce: true);
+                    var isArgument = QueryableBuilder.QueryTypeNameCompare("TestQuery");
+                    node = convertService.ConvertToData(expression, autoReduce: true, isArgument: isArgument);
                     var strNode = Json.Serialize(node);
 
                     // #2 Data to Code
@@ -80,7 +81,7 @@ namespace Vit.Linq.ExpressionTree.MsTest
                 throw new NotSupportedException("Method not support:" + methodName);
             };
 
-            var query = QueryableBuilder.Build<Person>(QueryExecutor);
+            var query = QueryableBuilder.Build<Person>(QueryExecutor,"TestQuery");
             return query;
         }
 
@@ -255,24 +256,23 @@ namespace Vit.Linq.ExpressionTree.MsTest
 
 
         [TestMethod]
-        public void Test_MethodCall_Any()
+        public void Test_MethodCall_Enumerable_Contains()
         {
-            #region Convert
+            #region Enumerable.Contains
             {
                 var query = GetQuery();
 
-                var persons = new List<Person> { new Person { id = 2 }, new Person { id = 3 } }
-                    //.AsQueryable()
-                    ;
+                var persons = new List<Person> { new Person { id = 2 }, new Person { id = 3 } };
+                var ids = persons.Where(p => p.id > 0).Select(p => p.id);
+
                 query = query
-                    .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 Where Any  
+                    .Where(m => ids.Contains(m.id)) // MethodCall Enumerable.Contains
                     ;
 
                 var list = query.ToList();
-                Assert.AreEqual(11, list.Count);
-                Assert.AreEqual(0, list.First().id);
-                Assert.AreEqual(10, list.Last().id);
+                Assert.AreEqual(2, list.Count);
+                Assert.AreEqual(2, list.First().id);
+                Assert.AreEqual(3, list.Last().id);
             }
             #endregion
 

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

@@ -1,6 +1,20 @@
 
 # TODO: 
 
+
+
+
+# MethodCall 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)) // MethodCall Enumerable.Any
+.Where(m => persons.Where(p => p.id > 0).Select(p=>p.id).Contains(m.id)) // MethodCall Enumerable.Contains
+//.Where(m => persons.Any(p => p.id == m.id)) // MethodCall Enumerable.Any
+//.Where(m => persons.Where(p => p.id > 0).Any(p => p.id == m.id)) // MethodCall Enumerable.Where  Enumerable.Any  
+
+
 # TotalCount  : Count without take and skip
 
 # CollectionStream. Select

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

@@ -24,7 +24,7 @@ namespace Vit.Orm.Sqlite.MsTest
         public static DbContext BuildInitedDatabase(string dbName)
         {
             //"data source=T:\\sample\\sers\\sqlite.db"
-            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "{dbName}.db");
+            var filePath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, $"{dbName}.db");
 
             if (File.Exists(filePath)) File.Delete(filePath);
             File.WriteAllBytes(filePath, new byte[0]);

+ 1 - 1
src/Vit.Linq/ExpressionTree/ComponentModel/ValueType.cs

@@ -261,7 +261,7 @@ namespace Vit.Linq.ExpressionTree.ComponentModel
                     {
                         return typeof(object);
                     }
-                default: return Type.GetType("System." + typeName);
+                default: return Type.GetType("System." + typeName) ?? typeof(object);
             }
         }
 

+ 112 - 73
src/Vit.Linq/ExpressionTree/DataConvertArgument.cs

@@ -5,127 +5,166 @@ using System.Linq;
 using System.Linq.Expressions;
 
 using Vit.Linq.ExpressionTree.ComponentModel;
+using Vit.Linq.ExpressionTree.ExpressionConvertor;
 
 namespace Vit.Linq.ExpressionTree
 {
-
+    public enum EValueType
+    {
+        /// <summary>
+        /// constant value like 1.5 or int[]{1,2,3}
+        /// </summary>
+        constant,
+        /// <summary>
+        /// QueryableArgument
+        /// </summary>
+        argument,
+        /// <summary>
+        /// like parameter from lambda argument  or combined value
+        /// </summary>
+        other,
+    }
     public class DataConvertArgument
     {
         public bool autoReduce { get; set; } = false;
 
+        public Func<ConstantExpression, bool> isArgument { get; set; }
 
-        public bool ReduceValue<T>(Expression expression, out T value)
+        public virtual bool IsArgument(ConstantExpression constant)
         {
-            try
-            {
-                if (autoReduce && CanCalculateToConstant(expression))
-                {
-                    value = (T)InvokeExpression(expression);
-                    return true;
-                }
-            }
-            catch (Exception ex)
+            if (isArgument != null)
+                return isArgument(constant);
+
+            var type = constant.Type;
+            //var value = constant.Value;
+            if (!type.IsArray && type.IsGenericType && typeof(IQueryable).IsAssignableFrom(type))
             {
+                //if (typeof(List<>) == type.GetGenericTypeDefinition())
+                //    return false;
+
+                return true;
             }
-            value = default;
             return false;
         }
 
-        public static object InvokeExpression(Expression expression) 
-        {
-            return Expression.Lambda(expression).Compile().DynamicInvoke();
-        }
-
-        public static bool CanCalculateToConstant(Expression expression) 
+        /// <summary>
+        /// TODO: use object hashcode to cache EValueType
+        /// </summary>
+        /// <param name="expression"></param>
+        /// <returns></returns>
+        protected EValueType GetEValueType(Expression expression)
         {
+            List<EValueType> childrenTypes;
             switch (expression)
             {
-                case null: return true;
+                case null: return EValueType.constant;
+
+                case ConstantExpression constant:
+                    {
+                        if (IsArgument(constant)) return EValueType.argument;
+                        return EValueType.constant;
+                    }
                 case MemberExpression member:
                     {
-                        return CanCalculateToConstant(member.Expression);
+                        return GetEValueType(member.Expression);
                     }
                 case UnaryExpression unary:
                     {
-                        //switch (unary.NodeType)
-                        //{
-                        //    case ExpressionType.Convert:
-                        //    case ExpressionType.Quote:
-                        //        return CouldExecuteToConst(unary.Operand);
-                        //}
-                        return CanCalculateToConstant(unary.Operand);
+                        return GetEValueType(unary.Operand) == EValueType.constant ? EValueType.constant : EValueType.other;
                     }
                 case BinaryExpression binary:
                     {
-                        return CanCalculateToConstant(binary.Left)&& CanCalculateToConstant(binary.Right);
-                    }
-                case ConstantExpression constant:
-                    {
-                        var type = expression.Type;
-                        var value = constant.Value;
-                        if (value == null) return true;
-                        if (IsQueryableArgument(type)) return false;
-                        return true;
+                        childrenTypes = new List<EValueType> { GetEValueType(binary.Left), GetEValueType(binary.Right) };
+                        break;
                     }
                 case NewArrayExpression newArray:
                     {
-                        return newArray.Expressions?.All(exp => CanCalculateToConstant(exp)) != false;
+                        childrenTypes = newArray.Expressions?.Select(GetEValueType).ToList();
+                        break;
                     }
                 case ListInitExpression listInit:
                     {
-                        return listInit.Initializers?.All(exp => CanCalculateToConstant(exp.Arguments[0])) != false;
+                        childrenTypes = listInit.Initializers?.Select(exp => GetEValueType(exp.Arguments[0])).ToList();
+                        break;
+                    }
+                case MethodCallExpression call:
+                    {
+                        childrenTypes = new();
+                        if (call.Arguments?.Any() == true) childrenTypes.AddRange(call.Arguments.Select(GetEValueType));
+                        if (call.Object != null) childrenTypes.Add(GetEValueType(call.Object));
+                        break;
                     }
+                default: return EValueType.other;
             }
-            return false;
+            if (childrenTypes?.Any() != true) return EValueType.constant;
+
+            if (childrenTypes.All(m => m == EValueType.constant)) return EValueType.constant;
+            return EValueType.other;
         }
 
-        #region Type
 
-        public static bool IsQueryableArgument(Type type)
+        public bool ReduceValue<T>(Expression expression, out T value)
         {
-            if (!type.IsArray && type.IsGenericType && typeof(IQueryable).IsAssignableFrom(type))
+            try
             {
-                //if (typeof(List<>) == type.GetGenericTypeDefinition())
-                //    return false;
-
-                return true;
+                if (autoReduce && CanCalculateToConstant(expression))
+                {
+                    value = (T)InvokeExpression(expression);
+                    return true;
+                }
             }
-
+            catch (Exception ex)
+            {
+            }
+            value = default;
             return false;
+        }
 
+        public static object InvokeExpression(Expression expression)
+        {
+            return Expression.Lambda(expression).Compile().DynamicInvoke();
         }
-        public static bool IsTransportableType(Type type)
+
+        public bool CanCalculateToConstant(Expression expression)
         {
-            if (IsBasicType(type)) return true;
+            return GetEValueType(expression) == EValueType.constant;
+        }
 
-            if (type.IsArray && IsTransportableType(type.GetElementType()))
-            {
-                return true;
-            }
+        #region Type
 
-            if (type.IsGenericType)
-            {
-                if (type.GetGenericArguments().Any(t => !IsTransportableType(t))) return false;
 
-                if (typeof(IList).IsAssignableFrom(type)
-                    || typeof(ICollection).IsAssignableFrom(type)
-                    )
-                    return true;
-            }
+        //public static bool IsTransportableType(Type type)
+        //{
+        //    if (IsBasicType(type)) return true;
 
-            return false;
-        }
+        //    if (type.IsArray && IsTransportableType(type.GetElementType()))
+        //    {
+        //        return true;
+        //    }
 
+        //    if (type.IsGenericType)
+        //    {
+        //        if (type.GetGenericArguments().Any(t => !IsTransportableType(t))) return false;
 
-        // is valueType of Nullable 
-        public static bool IsBasicType(Type type)
-        {
-            return
-                type.IsEnum || // enum
-                type == typeof(string) || // string
-                type.IsValueType ||  //int
-                (type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition()); // int?
-        }
+        //        if (typeof(IList).IsAssignableFrom(type)
+        //            || typeof(ICollection).IsAssignableFrom(type)
+        //            )
+        //            return true;
+        //    }
+
+        //    return false;
+        //}
+
+
+        //// is valueType of Nullable 
+        //public static bool IsBasicType(Type type)
+        //{
+        //    return
+        //        type.IsEnum || // enum
+        //        type == typeof(string) || // string
+        //        type.IsValueType ||  //int
+        //        (type.IsGenericType && typeof(Nullable<>) == type.GetGenericTypeDefinition()); // int?
+        //}
 
 
         #endregion
@@ -190,7 +229,7 @@ namespace Vit.Linq.ExpressionTree
 
 
 
-      
+
     }
 
 

+ 4 - 4
src/Vit.Linq/ExpressionTree/ExpressionConvertService.ToData.cs

@@ -8,14 +8,14 @@ namespace Vit.Linq.ExpressionTree
 {
     public partial class ExpressionConvertService
     {
-        public ExpressionNode ConvertToData(Expression expression, bool autoReduce = true)
+        public ExpressionNode ConvertToData(Expression expression, bool autoReduce = true, Func<ConstantExpression, bool> isArgument = null)
         {
-            return ConvertToData(expression, out _, autoReduce);
+            return ConvertToData(expression, out _, autoReduce: autoReduce, isArgument: isArgument);
         }
 
-        public ExpressionNode ConvertToData(Expression expression, out ParamterInfo[] parameters, bool autoReduce = true)
+        public ExpressionNode ConvertToData(Expression expression, out ParamterInfo[] parameters, bool autoReduce = true, Func<ConstantExpression, bool> isArgument = null)
         {
-            var arg = new DataConvertArgument { convertService = this, autoReduce = autoReduce };
+            var arg = new DataConvertArgument { convertService = this, autoReduce = autoReduce, isArgument = isArgument };
 
             ExpressionNode body = ConvertToData(arg, expression);
 

+ 28 - 23
src/Vit.Linq/ExpressionTree/ExpressionConvertor/Constant.cs

@@ -12,31 +12,36 @@ namespace Vit.Linq.ExpressionTree.ExpressionConvertor
     {
         public ExpressionNode ConvertToData(DataConvertArgument arg, Expression expression)
         {
-            if (expression is ConstantExpression constant)
+            switch (expression)
             {
-                var type = expression.Type;
-                var value = constant.Value;
-                if (value != null && DataConvertArgument.IsQueryableArgument(type))
-                {
-                    return arg.CreateParameter(value, type);
-                }
-                return ExpressionNode.Constant(value: constant.Value, type: type);
+                case ConstantExpression constant:
+                    {
+                        var type = expression.Type;
+                        var value = constant.Value;
+                        if (arg.IsArgument(constant))
+                        {
+                            return arg.CreateParameter(value, type);
+                        }
+                        return ExpressionNode.Constant(value: constant.Value, type: type);
+                    }
+                case NewArrayExpression newArray:
+                case ListInitExpression listInit:
+                    {
+                        if (arg.CanCalculateToConstant(expression))
+                        {
+                            return ExpressionNode.Constant(value: DataConvertArgument.InvokeExpression(expression), type: expression.Type);
+                        }
+                        break;
+                    }
+                default:
+                    {
+                        if (arg.autoReduce && arg.CanCalculateToConstant(expression))
+                        {
+                            return ExpressionNode.Constant(value: DataConvertArgument.InvokeExpression(expression), type: expression.Type);
+                        }
+                        break;
+                    }
             }
-            else if (expression is NewArrayExpression newArray)
-            {
-                if (DataConvertArgument.CanCalculateToConstant(newArray))
-                {
-                    return ExpressionNode.Constant(value: DataConvertArgument.InvokeExpression(expression), type: expression.Type);
-                }
-            }
-            else if (expression is ListInitExpression listInit)
-            {
-                if (DataConvertArgument.CanCalculateToConstant(listInit))
-                {
-                    return ExpressionNode.Constant(value: DataConvertArgument.InvokeExpression(expression), type: expression.Type);
-                }
-            }
-
             return null;
         }
 

+ 23 - 5
src/Vit.Linq/QueryableBuilder.cs

@@ -3,15 +3,27 @@ using System.Collections.Generic;
 using System.Collections;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Reflection;
 
 namespace Vit.Linq
 {
     public static class QueryableBuilder
     {
-        public static IQueryable<Model> Build<Model>(Func<Expression, Type, object> QueryExecutor)
+        public static IQueryable<Model> Build<Model>(Func<Expression, Type, object> QueryExecutor, string queryTypeName = null)
         {
             var queryProvider = new QueryProvider(QueryExecutor);
-            return new OrderedQueryable<Model>(queryProvider);
+            return new OrderedQueryable<Model>(queryProvider, queryTypeName);
+        }
+
+        public static string GetQueryTypeName (IQueryable  query)
+        {
+            return (query as IQueryType)?.queryTypeName;
+        }
+
+
+        public static Func<ConstantExpression, bool> QueryTypeNameCompare(string queryTypeName)
+        {
+            return (exp) => GetQueryTypeName(exp?.Value as IQueryable) == queryTypeName;
         }
 
 
@@ -61,16 +73,22 @@ namespace Vit.Linq
         }
     }
 
-
-    internal class OrderedQueryable<T> : IOrderedQueryable<T>
+    interface IQueryType 
+    {
+        string queryTypeName { get; }
+    }
+    internal class OrderedQueryable<T> : IOrderedQueryable<T>, IQueryType
     {
         protected readonly Expression _expression;
         protected readonly QueryProvider _provider;
 
-        public OrderedQueryable(QueryProvider provider)
+        public string queryTypeName { get;  set; }
+
+        public OrderedQueryable(QueryProvider provider, string queryTypeName = null)
         {
             _provider = provider ?? throw new ArgumentNullException(nameof(provider));
             _expression = Expression.Constant(this);
+            this.queryTypeName = queryTypeName;
         }
 
         public OrderedQueryable(QueryProvider provider, Expression expression)

+ 3 - 2
src/Vit.Orm/Sql/SqlDbSet.cs

@@ -68,7 +68,8 @@ namespace Vit.Orm.Sql
             {
                 // #1 convert to ExpressionNode
                 // (query) => query.Where().OrderBy().Skip().Take().Select().ToList();
-                ExpressionNode node = dbContext.convertService.ConvertToData(expression, autoReduce: true);
+                var isArgument = QueryableBuilder.QueryTypeNameCompare("SqlDbSet");
+                ExpressionNode node = dbContext.convertService.ConvertToData(expression, autoReduce: true, isArgument: isArgument);
                 var strNode = Json.Serialize(node);
 
 
@@ -126,7 +127,7 @@ namespace Vit.Orm.Sql
                 }
                 throw new NotSupportedException("not supported query type: " + joinedStream.method);
             };
-            return QueryableBuilder.Build<Entity>(QueryExecutor);
+            return QueryableBuilder.Build<Entity>(QueryExecutor, "SqlDbSet");
         }
 
         int BatchUpdate(StreamToUpdate streamToUpdate, Type entityType)