mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 Array.Any(x => x.id == a.Id && ..) 表达式树解析;#243
This commit is contained in:
		@@ -786,5 +786,14 @@
 | 
				
			|||||||
            <param name="that"></param>
 | 
					            <param name="that"></param>
 | 
				
			||||||
            <returns></returns>
 | 
					            <returns></returns>
 | 
				
			||||||
        </member>
 | 
					        </member>
 | 
				
			||||||
 | 
					        <member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{FreeSql.FluentDataFilter},System.Reflection.Assembly[])">
 | 
				
			||||||
 | 
					            <summary>
 | 
				
			||||||
 | 
					            批量注入 Repository,可以参考代码自行调整
 | 
				
			||||||
 | 
					            </summary>
 | 
				
			||||||
 | 
					            <param name="services"></param>
 | 
				
			||||||
 | 
					            <param name="globalDataFilter"></param>
 | 
				
			||||||
 | 
					            <param name="assemblies"></param>
 | 
				
			||||||
 | 
					            <returns></returns>
 | 
				
			||||||
 | 
					        </member>
 | 
				
			||||||
    </members>
 | 
					    </members>
 | 
				
			||||||
</doc>
 | 
					</doc>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,75 @@ namespace FreeSql.Tests.SqliteExpression
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void ArrayAny()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var fsql = g.sqlite;
 | 
				
			||||||
 | 
					            fsql.Delete<ArrayAny01>().Where("1=1").ExecuteAffrows();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var t1 = fsql.Select<ArrayAny01>().Where(a => new[] {
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name01", Click1 = 1 },
 | 
				
			||||||
 | 
					            }.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
 | 
				
			||||||
 | 
					            Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click"" 
 | 
				
			||||||
 | 
					FROM ""ArrayAny01"" a 
 | 
				
			||||||
 | 
					WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10))", t1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var t2 = fsql.Select<ArrayAny01>().Where(a => new[] {
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name01", Click1 = 1 },
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name02", Click1 = 2 },
 | 
				
			||||||
 | 
					            }.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
 | 
				
			||||||
 | 
					            Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click"" 
 | 
				
			||||||
 | 
					FROM ""ArrayAny01"" a 
 | 
				
			||||||
 | 
					WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10) OR ('name02' = a.""Name"" AND 2 = a.""Click"" OR a.""Click"" > 10))", t1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var aa03 = new[] {
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name01", Click1 = 1 },
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var t3 = fsql.Select<ArrayAny01>().Where(a => aa03.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
 | 
				
			||||||
 | 
					            Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click"" 
 | 
				
			||||||
 | 
					FROM ""ArrayAny01"" a 
 | 
				
			||||||
 | 
					WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10))", t1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var aa04 = new[] {
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name01", Click1 = 1 },
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name02", Click1 = 2 },
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var t4 = fsql.Select<ArrayAny01>().Where(a => aa04.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
 | 
				
			||||||
 | 
					            Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click"" 
 | 
				
			||||||
 | 
					FROM ""ArrayAny01"" a 
 | 
				
			||||||
 | 
					WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10) OR ('name02' = a.""Name"" AND 2 = a.""Click"" OR a.""Click"" > 10))", t1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // List
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var aa05 = new List<ArrayAny02> {
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name01", Click1 = 1 },
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var t5 = fsql.Select<ArrayAny01>().Where(a => aa05.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
 | 
				
			||||||
 | 
					            Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click"" 
 | 
				
			||||||
 | 
					FROM ""ArrayAny01"" a 
 | 
				
			||||||
 | 
					WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10))", t1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var aa06 = new List<ArrayAny02> {
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name01", Click1 = 1 },
 | 
				
			||||||
 | 
					                new ArrayAny02 { Name1 = "name02", Click1 = 2 },
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var t6 = fsql.Select<ArrayAny01>().Where(a => aa06.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
 | 
				
			||||||
 | 
					            Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click"" 
 | 
				
			||||||
 | 
					FROM ""ArrayAny01"" a 
 | 
				
			||||||
 | 
					WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10) OR ('name02' = a.""Name"" AND 2 = a.""Click"" OR a.""Click"" > 10))", t1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        class ArrayAny01
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public Guid Id { get; set; }
 | 
				
			||||||
 | 
					            public string Name { get; set; }
 | 
				
			||||||
 | 
					            public long Click { get; set; }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        class ArrayAny02
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            public string Name1 { get; set; }
 | 
				
			||||||
 | 
					            public long Click1 { get; set; }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void Div()
 | 
					        public void Div()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -906,6 +906,30 @@ namespace FreeSql.Internal
 | 
				
			|||||||
                        if (exp3.Arguments.Count > 0 && exp3.Object != null) return ExpressionBinary("=", exp3.Object, exp3.Arguments[0], tsc);
 | 
					                        if (exp3.Arguments.Count > 0 && exp3.Object != null) return ExpressionBinary("=", exp3.Object, exp3.Arguments[0], tsc);
 | 
				
			||||||
                        if (exp3.Arguments.Count > 1 && exp3.Method.DeclaringType == typeof(object)) return ExpressionBinary("=", exp3.Arguments[0], exp3.Arguments[1], tsc);
 | 
					                        if (exp3.Arguments.Count > 1 && exp3.Method.DeclaringType == typeof(object)) return ExpressionBinary("=", exp3.Arguments[0], exp3.Arguments[1], tsc);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (exp3.Method.Name == "Any" && exp3.Method.DeclaringType == typeof(Enumerable))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        //Where(a => idArray.Any(p => (a.Id == p.Key || a.RoleName == p.Key) && a.RoleType == p.Type))
 | 
				
			||||||
 | 
					                        var exp3MethodGenArgs = exp3.Method.GetGenericArguments();
 | 
				
			||||||
 | 
					                        var exp3MethodArgs = exp3.Method.GetParameters();
 | 
				
			||||||
 | 
					                        if (exp3MethodGenArgs.Length == 1 && exp3MethodArgs.Length == 2 && exp3MethodArgs[1].ParameterType == typeof(Func<,>).MakeGenericType(exp3MethodGenArgs[0], typeof(bool)))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            var exp3Value = ExpressionGetValue(exp3.Arguments[0], out var exp3ValueSuccess);
 | 
				
			||||||
 | 
					                            if (exp3ValueSuccess)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                if (exp3Value == null) return "1=2";
 | 
				
			||||||
 | 
					                                var exp3ValueIE = exp3Value as IEnumerable;
 | 
				
			||||||
 | 
					                                var exp3NewExpVisitor = new ReplaceParameterVisitor();
 | 
				
			||||||
 | 
					                                var exp3sb = new StringBuilder();
 | 
				
			||||||
 | 
					                                foreach (var exp3ValueItem in exp3ValueIE)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    var exp3NewExp = exp3NewExpVisitor.Modify(exp3.Arguments[1] as LambdaExpression, Expression.Constant(exp3ValueItem, exp3MethodGenArgs[0]));
 | 
				
			||||||
 | 
					                                    exp3sb.Append(" OR ").Append(ExpressionLambdaToSql(exp3NewExp, tsc));
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if (exp3sb.Length == 0) return "1=2";
 | 
				
			||||||
 | 
					                                return exp3sb.Remove(0, 4).ToString();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    if (callType.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`"))
 | 
					                    if (callType.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`"))
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        switch (exp3.Method.Name)
 | 
					                        switch (exp3.Method.Name)
 | 
				
			||||||
@@ -1736,6 +1760,86 @@ namespace FreeSql.Internal
 | 
				
			|||||||
        public abstract string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc);
 | 
					        public abstract string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc);
 | 
				
			||||||
        public string ExpressionConstDateTime(Expression exp) => exp is ConstantExpression operandExpConst ? formatSql(Utils.GetDataReaderValue(typeof(DateTime), operandExpConst.Value), null, null, null) : null;
 | 
					        public string ExpressionConstDateTime(Expression exp) => exp is ConstantExpression operandExpConst ? formatSql(Utils.GetDataReaderValue(typeof(DateTime), operandExpConst.Value), null, null, null) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static object ExpressionGetValue(Expression exp, out bool success)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            success = true;
 | 
				
			||||||
 | 
					            var expStack = new Stack<Expression>();
 | 
				
			||||||
 | 
					            var expStackConstOrMemberCount = 1;
 | 
				
			||||||
 | 
					            var exp2 = exp;
 | 
				
			||||||
 | 
					            while (true)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                switch (exp2?.NodeType)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    case ExpressionType.Constant:
 | 
				
			||||||
 | 
					                        expStack.Push(exp2);
 | 
				
			||||||
 | 
					                        expStackConstOrMemberCount++;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case ExpressionType.Parameter:
 | 
				
			||||||
 | 
					                        expStack.Push(exp2);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case ExpressionType.MemberAccess:
 | 
				
			||||||
 | 
					                        expStack.Push(exp2);
 | 
				
			||||||
 | 
					                        exp2 = (exp2 as MemberExpression).Expression;
 | 
				
			||||||
 | 
					                        expStackConstOrMemberCount++;
 | 
				
			||||||
 | 
					                        if (exp2 == null) break;
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    case ExpressionType.Call:
 | 
				
			||||||
 | 
					                        var callExp = exp2 as MethodCallExpression;
 | 
				
			||||||
 | 
					                        expStack.Push(exp2);
 | 
				
			||||||
 | 
					                        exp2 = callExp.Object;
 | 
				
			||||||
 | 
					                        if (exp2 == null) break;
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    case ExpressionType.TypeAs:
 | 
				
			||||||
 | 
					                    case ExpressionType.Convert:
 | 
				
			||||||
 | 
					                        var oper2 = (exp2 as UnaryExpression).Operand;
 | 
				
			||||||
 | 
					                        if (oper2.NodeType == ExpressionType.Parameter)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            var oper2Parm = oper2 as ParameterExpression;
 | 
				
			||||||
 | 
					                            expStack.Push(exp2.Type.IsAbstract || exp2.Type.IsInterface ? oper2Parm : Expression.Parameter(exp2.Type, oper2Parm.Name));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                            expStack.Push(oper2);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (expStack.Any() && expStack.First().NodeType != ExpressionType.Parameter)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (expStackConstOrMemberCount == expStack.Count)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    object firstValue = null;
 | 
				
			||||||
 | 
					                    switch (expStack.First().NodeType)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        case ExpressionType.Constant:
 | 
				
			||||||
 | 
					                            var expStackFirst = expStack.Pop() as ConstantExpression;
 | 
				
			||||||
 | 
					                            firstValue = expStackFirst?.Value;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case ExpressionType.MemberAccess:
 | 
				
			||||||
 | 
					                            var expStackFirstMem = expStack.First() as MemberExpression;
 | 
				
			||||||
 | 
					                            if (expStackFirstMem.Expression?.NodeType == ExpressionType.Constant)
 | 
				
			||||||
 | 
					                                firstValue = (expStackFirstMem.Expression as ConstantExpression)?.Value;
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                return Expression.Lambda(exp).Compile().DynamicInvoke();
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    while (expStack.Any())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var expStackItem = expStack.Pop() as MemberExpression;
 | 
				
			||||||
 | 
					                        if (expStackItem.Member.MemberType == MemberTypes.Property)
 | 
				
			||||||
 | 
					                            firstValue = ((PropertyInfo)expStackItem.Member).GetValue(firstValue, null);
 | 
				
			||||||
 | 
					                        else if (expStackItem.Member.MemberType == MemberTypes.Field)
 | 
				
			||||||
 | 
					                            firstValue = ((FieldInfo)expStackItem.Member).GetValue(firstValue);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return firstValue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return Expression.Lambda(exp).Compile().DynamicInvoke();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (exp.IsParameter() == false)
 | 
				
			||||||
 | 
					                return Expression.Lambda(exp).Compile().DynamicInvoke();
 | 
				
			||||||
 | 
					            success = false;
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public enum ExpressionStyle
 | 
					        public enum ExpressionStyle
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Where, AsSelect, SelectColumns
 | 
					            Where, AsSelect, SelectColumns
 | 
				
			||||||
@@ -1889,18 +1993,18 @@ namespace FreeSql.Internal
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        public class ReplaceParameterVisitor : ExpressionVisitor
 | 
					        public class ReplaceParameterVisitor : ExpressionVisitor
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            private ParameterExpression parameter;
 | 
					            private Expression _replaceExp;
 | 
				
			||||||
            private ParameterExpression oldParameter;
 | 
					            private ParameterExpression oldParameter;
 | 
				
			||||||
            public Expression Modify(LambdaExpression lambda, ParameterExpression parameter)
 | 
					            public Expression Modify(LambdaExpression lambda, Expression replaceExp)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                this.parameter = parameter;
 | 
					                this._replaceExp = replaceExp;
 | 
				
			||||||
                this.oldParameter = lambda.Parameters.FirstOrDefault();
 | 
					                this.oldParameter = lambda.Parameters.FirstOrDefault();
 | 
				
			||||||
                return Visit(lambda.Body);
 | 
					                return Visit(lambda.Body);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            protected override Expression VisitMember(MemberExpression node)
 | 
					            protected override Expression VisitMember(MemberExpression node)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (node.Expression?.NodeType == ExpressionType.Parameter && node.Expression == oldParameter)
 | 
					                if (node.Expression?.NodeType == ExpressionType.Parameter && node.Expression == oldParameter)
 | 
				
			||||||
                    return Expression.Property(parameter, node.Member.Name);
 | 
					                    return Expression.Property(_replaceExp, node.Member.Name);
 | 
				
			||||||
                return base.VisitMember(node);
 | 
					                return base.VisitMember(node);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user