diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 4335acb5..d34b34ef 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -786,5 +786,14 @@ + + + 批量注入 Repository,可以参考代码自行调整 + + + + + + diff --git a/FreeSql.Tests/FreeSql.Tests/Sqlite/SqliteExpression/OtherTest.cs b/FreeSql.Tests/FreeSql.Tests/Sqlite/SqliteExpression/OtherTest.cs index 03241025..c08f78fd 100644 --- a/FreeSql.Tests/FreeSql.Tests/Sqlite/SqliteExpression/OtherTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/Sqlite/SqliteExpression/OtherTest.cs @@ -15,6 +15,75 @@ namespace FreeSql.Tests.SqliteExpression { } + [Fact] + public void ArrayAny() + { + var fsql = g.sqlite; + fsql.Delete().Where("1=1").ExecuteAffrows(); + + var t1 = fsql.Select().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().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().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().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 { + new ArrayAny02 { Name1 = "name01", Click1 = 1 }, + }; + var t5 = fsql.Select().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 { + new ArrayAny02 { Name1 = "name01", Click1 = 1 }, + new ArrayAny02 { Name1 = "name02", Click1 = 2 }, + }; + var t6 = fsql.Select().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] public void Div() { diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 437190a9..75578839 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -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 > 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`")) { switch (exp3.Method.Name) @@ -1736,6 +1760,86 @@ namespace FreeSql.Internal 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 static object ExpressionGetValue(Expression exp, out bool success) + { + success = true; + var expStack = new Stack(); + 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 { Where, AsSelect, SelectColumns @@ -1889,18 +1993,18 @@ namespace FreeSql.Internal } public class ReplaceParameterVisitor : ExpressionVisitor { - private ParameterExpression parameter; + private Expression _replaceExp; 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(); return Visit(lambda.Body); } protected override Expression VisitMember(MemberExpression node) { 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); } }