Skip to content

Commit 3a75332

Browse files
committed
- 增加 Array.Any(x => x.id == a.Id && ..) 表达式树解析;#243
1 parent 5a9c92b commit 3a75332

File tree

3 files changed

+186
-4
lines changed

3 files changed

+186
-4
lines changed

FreeSql.DbContext/FreeSql.DbContext.xml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

FreeSql.Tests/FreeSql.Tests/Sqlite/SqliteExpression/OtherTest.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,75 @@ public OtherTest()
1515
{
1616
}
1717

18+
[Fact]
19+
public void ArrayAny()
20+
{
21+
var fsql = g.sqlite;
22+
fsql.Delete<ArrayAny01>().Where("1=1").ExecuteAffrows();
23+
24+
var t1 = fsql.Select<ArrayAny01>().Where(a => new[] {
25+
new ArrayAny02 { Name1 = "name01", Click1 = 1 },
26+
}.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
27+
Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click""
28+
FROM ""ArrayAny01"" a
29+
WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10))", t1);
30+
31+
var t2 = fsql.Select<ArrayAny01>().Where(a => new[] {
32+
new ArrayAny02 { Name1 = "name01", Click1 = 1 },
33+
new ArrayAny02 { Name1 = "name02", Click1 = 2 },
34+
}.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
35+
Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click""
36+
FROM ""ArrayAny01"" a
37+
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);
38+
39+
var aa03 = new[] {
40+
new ArrayAny02 { Name1 = "name01", Click1 = 1 },
41+
};
42+
var t3 = fsql.Select<ArrayAny01>().Where(a => aa03.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
43+
Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click""
44+
FROM ""ArrayAny01"" a
45+
WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10))", t1);
46+
47+
var aa04 = new[] {
48+
new ArrayAny02 { Name1 = "name01", Click1 = 1 },
49+
new ArrayAny02 { Name1 = "name02", Click1 = 2 },
50+
};
51+
var t4 = fsql.Select<ArrayAny01>().Where(a => aa04.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
52+
Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click""
53+
FROM ""ArrayAny01"" a
54+
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);
55+
56+
// List
57+
58+
var aa05 = new List<ArrayAny02> {
59+
new ArrayAny02 { Name1 = "name01", Click1 = 1 },
60+
};
61+
var t5 = fsql.Select<ArrayAny01>().Where(a => aa05.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
62+
Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click""
63+
FROM ""ArrayAny01"" a
64+
WHERE (('name01' = a.""Name"" AND 1 = a.""Click"" OR a.""Click"" > 10))", t1);
65+
66+
var aa06 = new List<ArrayAny02> {
67+
new ArrayAny02 { Name1 = "name01", Click1 = 1 },
68+
new ArrayAny02 { Name1 = "name02", Click1 = 2 },
69+
};
70+
var t6 = fsql.Select<ArrayAny01>().Where(a => aa06.Any(b => b.Name1 == a.Name && b.Click1 == a.Click || a.Click > 10)).ToSql();
71+
Assert.Equal(@"SELECT a.""Id"", a.""Name"", a.""Click""
72+
FROM ""ArrayAny01"" a
73+
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);
74+
}
75+
class ArrayAny01
76+
{
77+
public Guid Id { get; set; }
78+
public string Name { get; set; }
79+
public long Click { get; set; }
80+
}
81+
class ArrayAny02
82+
{
83+
public string Name1 { get; set; }
84+
public long Click1 { get; set; }
85+
}
86+
1887
[Fact]
1988
public void Div()
2089
{

FreeSql/Internal/CommonExpression.cs

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,30 @@ public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc)
906906
if (exp3.Arguments.Count > 0 && exp3.Object != null) return ExpressionBinary("=", exp3.Object, exp3.Arguments[0], tsc);
907907
if (exp3.Arguments.Count > 1 && exp3.Method.DeclaringType == typeof(object)) return ExpressionBinary("=", exp3.Arguments[0], exp3.Arguments[1], tsc);
908908
}
909+
if (exp3.Method.Name == "Any" && exp3.Method.DeclaringType == typeof(Enumerable))
910+
{
911+
//Where(a => idArray.Any(p => (a.Id == p.Key || a.RoleName == p.Key) && a.RoleType == p.Type))
912+
var exp3MethodGenArgs = exp3.Method.GetGenericArguments();
913+
var exp3MethodArgs = exp3.Method.GetParameters();
914+
if (exp3MethodGenArgs.Length == 1 && exp3MethodArgs.Length == 2 && exp3MethodArgs[1].ParameterType == typeof(Func<,>).MakeGenericType(exp3MethodGenArgs[0], typeof(bool)))
915+
{
916+
var exp3Value = ExpressionGetValue(exp3.Arguments[0], out var exp3ValueSuccess);
917+
if (exp3ValueSuccess)
918+
{
919+
if (exp3Value == null) return "1=2";
920+
var exp3ValueIE = exp3Value as IEnumerable;
921+
var exp3NewExpVisitor = new ReplaceParameterVisitor();
922+
var exp3sb = new StringBuilder();
923+
foreach (var exp3ValueItem in exp3ValueIE)
924+
{
925+
var exp3NewExp = exp3NewExpVisitor.Modify(exp3.Arguments[1] as LambdaExpression, Expression.Constant(exp3ValueItem, exp3MethodGenArgs[0]));
926+
exp3sb.Append(" OR ").Append(ExpressionLambdaToSql(exp3NewExp, tsc));
927+
}
928+
if (exp3sb.Length == 0) return "1=2";
929+
return exp3sb.Remove(0, 4).ToString();
930+
}
931+
}
932+
}
909933
if (callType.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`"))
910934
{
911935
switch (exp3.Method.Name)
@@ -1736,6 +1760,86 @@ public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc)
17361760
public abstract string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc);
17371761
public string ExpressionConstDateTime(Expression exp) => exp is ConstantExpression operandExpConst ? formatSql(Utils.GetDataReaderValue(typeof(DateTime), operandExpConst.Value), null, null, null) : null;
17381762

1763+
public static object ExpressionGetValue(Expression exp, out bool success)
1764+
{
1765+
success = true;
1766+
var expStack = new Stack<Expression>();
1767+
var expStackConstOrMemberCount = 1;
1768+
var exp2 = exp;
1769+
while (true)
1770+
{
1771+
switch (exp2?.NodeType)
1772+
{
1773+
case ExpressionType.Constant:
1774+
expStack.Push(exp2);
1775+
expStackConstOrMemberCount++;
1776+
break;
1777+
case ExpressionType.Parameter:
1778+
expStack.Push(exp2);
1779+
break;
1780+
case ExpressionType.MemberAccess:
1781+
expStack.Push(exp2);
1782+
exp2 = (exp2 as MemberExpression).Expression;
1783+
expStackConstOrMemberCount++;
1784+
if (exp2 == null) break;
1785+
continue;
1786+
case ExpressionType.Call:
1787+
var callExp = exp2 as MethodCallExpression;
1788+
expStack.Push(exp2);
1789+
exp2 = callExp.Object;
1790+
if (exp2 == null) break;
1791+
continue;
1792+
case ExpressionType.TypeAs:
1793+
case ExpressionType.Convert:
1794+
var oper2 = (exp2 as UnaryExpression).Operand;
1795+
if (oper2.NodeType == ExpressionType.Parameter)
1796+
{
1797+
var oper2Parm = oper2 as ParameterExpression;
1798+
expStack.Push(exp2.Type.IsAbstract || exp2.Type.IsInterface ? oper2Parm : Expression.Parameter(exp2.Type, oper2Parm.Name));
1799+
}
1800+
else
1801+
expStack.Push(oper2);
1802+
break;
1803+
}
1804+
break;
1805+
}
1806+
if (expStack.Any() && expStack.First().NodeType != ExpressionType.Parameter)
1807+
{
1808+
if (expStackConstOrMemberCount == expStack.Count)
1809+
{
1810+
object firstValue = null;
1811+
switch (expStack.First().NodeType)
1812+
{
1813+
case ExpressionType.Constant:
1814+
var expStackFirst = expStack.Pop() as ConstantExpression;
1815+
firstValue = expStackFirst?.Value;
1816+
break;
1817+
case ExpressionType.MemberAccess:
1818+
var expStackFirstMem = expStack.First() as MemberExpression;
1819+
if (expStackFirstMem.Expression?.NodeType == ExpressionType.Constant)
1820+
firstValue = (expStackFirstMem.Expression as ConstantExpression)?.Value;
1821+
else
1822+
return Expression.Lambda(exp).Compile().DynamicInvoke();
1823+
break;
1824+
}
1825+
while (expStack.Any())
1826+
{
1827+
var expStackItem = expStack.Pop() as MemberExpression;
1828+
if (expStackItem.Member.MemberType == MemberTypes.Property)
1829+
firstValue = ((PropertyInfo)expStackItem.Member).GetValue(firstValue, null);
1830+
else if (expStackItem.Member.MemberType == MemberTypes.Field)
1831+
firstValue = ((FieldInfo)expStackItem.Member).GetValue(firstValue);
1832+
}
1833+
return firstValue;
1834+
}
1835+
return Expression.Lambda(exp).Compile().DynamicInvoke();
1836+
}
1837+
if (exp.IsParameter() == false)
1838+
return Expression.Lambda(exp).Compile().DynamicInvoke();
1839+
success = false;
1840+
return null;
1841+
}
1842+
17391843
public enum ExpressionStyle
17401844
{
17411845
Where, AsSelect, SelectColumns
@@ -1889,18 +1993,18 @@ public string GetWhereCascadeSql(SelectTableInfo tb, List<GlobalFilter.Item> fil
18891993
}
18901994
public class ReplaceParameterVisitor : ExpressionVisitor
18911995
{
1892-
private ParameterExpression parameter;
1996+
private Expression _replaceExp;
18931997
private ParameterExpression oldParameter;
1894-
public Expression Modify(LambdaExpression lambda, ParameterExpression parameter)
1998+
public Expression Modify(LambdaExpression lambda, Expression replaceExp)
18951999
{
1896-
this.parameter = parameter;
2000+
this._replaceExp = replaceExp;
18972001
this.oldParameter = lambda.Parameters.FirstOrDefault();
18982002
return Visit(lambda.Body);
18992003
}
19002004
protected override Expression VisitMember(MemberExpression node)
19012005
{
19022006
if (node.Expression?.NodeType == ExpressionType.Parameter && node.Expression == oldParameter)
1903-
return Expression.Property(parameter, node.Member.Name);
2007+
return Expression.Property(_replaceExp, node.Member.Name);
19042008
return base.VisitMember(node);
19052009
}
19062010
}

0 commit comments

Comments
 (0)