带有子查询的表达式树
Expression Trees with subquery
我的objective 是为动态全文搜索创建一个子查询表达式树。在 SQL 中相当于
SELECT *
FROM MV
WHERE MV.ID IN (SELECT ID
FROM MVF
WHERE title = "foo" OR Description = "foo")
所以基本思想是创建 FTS 子查询,从中获取 ID 并将它们用于 In 谓词。我的问题是第二部分。
// Get subquery for FTS tables
ParameterExpression ftsParam = Expression.Parameter(typeof(MVF), "mvfts");
var wphrase = Expression.Constant("foo");
var methodInfo = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
var ftsID = Expression.Property(ftsParam, "ID");
var ftsTitle = Expression.Property(ftsParam, "Title");
var ftsDescrip = Expression.Property(ftsParam, "Description");
var texp = Expression.Call(ftsTitle, methodInfo, wphrase);
var dexp = Expression.Call(ftsDescrip, methodInfo, wphrase);
var ftsExp = Expression.Or(texp, dexp);
// Now get ids from the above fts resultset
// THE ASSIGNMENT BELOW THROWS
var selectExp = Expression.Call(typeof(IEnumerable<MVF>), "Select", new Type[]
{
typeof(long)
},
ftsExp,
Expression.Lambda<Func<MFV, long>>(
ftsID,
ftsParam
)
);
// Now set up MV table reference
ParameterExpression vParam = Expression.Parameter(typeof(MV), "mv");
var mvID = Expression.Property(vParam, "MVID");
var containsInfo = typeof(IEnumerable<long>).GetMethod("Contains", new Type[] { typeof(long) });
// Now combine expression to get those mvs with ids in the result set of fts query
var containsExp = Expression.Call(selectExp, containsInfo, mvID);
return Expression.Lambda<Func<MV, bool>>(containsExp, vParam);
例外情况是:
No generic method 'Select' on type
'System.Collections.Generic.IEnumerable`1[MVF]' is compatible with the
supplied type arguments and arguments. No type arguments should be
provided if the method is non-generic.
typeof(IEnumerable<long>)
没有定义 select 方法。 Linq Select 方法是扩展方法,不能通过反射直接看到。定义它们的 class 是 Enumerable
。但是 typeof(Enumerable)
也不起作用,因为该方法是通用的。您必须首先从 class 可枚举中获取通用方法,然后使用 MethodInfo.MakeGenericMethod
创建一个采用长参数的方法。
var method = typeof(Enumerable).GetMethods().First(m => m.Name == "Select" &&
m.GetParameters().Last().ParameterType.GetGenericArguments().Length == 2);
var genericSelectFromLongToLong = method.MakeGenericMethod(new Type[] {typeof(long), typeof(long)});
请注意,检查通用参数而不是参数计数可能会更好。
相关表达式所需的两种方法都是静态泛型扩展方法(最重要的是 static 和 generic) Enumerable
class:
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector
)
public static bool Contains<TSource>(
this IEnumerable<TSource> source,
TSource value
)
"calling" 此类方法最方便的方法是以下 Expression.Call
方法重载:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
Type type
参数是 class 定义被调用方法的类型(在本例中是 typeof(Enumerable)
),Type[] typeArguments
是具有类型的数组泛型类型参数(非泛型方法为空,Select
应为 { typeof(TSource), typeof(TResult) }
,Contains
应为 { typeof(TSource) }
)。
将它应用到您的场景中:
var selectExp = Expression.Call(
typeof(Enumerable),
"Select",
new { typeof(MFV), typeof(long) },
ftsExp,
Expression.Lambda<Func<MFV, long>>(ftsID, ftsParam)
);
和
var containsExp = Expression.Call(
typeof(Enumerable),
"Contains",
new [] { typeof(long) },
selectExp,
mvID
);
我的objective 是为动态全文搜索创建一个子查询表达式树。在 SQL 中相当于
SELECT *
FROM MV
WHERE MV.ID IN (SELECT ID
FROM MVF
WHERE title = "foo" OR Description = "foo")
所以基本思想是创建 FTS 子查询,从中获取 ID 并将它们用于 In 谓词。我的问题是第二部分。
// Get subquery for FTS tables
ParameterExpression ftsParam = Expression.Parameter(typeof(MVF), "mvfts");
var wphrase = Expression.Constant("foo");
var methodInfo = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
var ftsID = Expression.Property(ftsParam, "ID");
var ftsTitle = Expression.Property(ftsParam, "Title");
var ftsDescrip = Expression.Property(ftsParam, "Description");
var texp = Expression.Call(ftsTitle, methodInfo, wphrase);
var dexp = Expression.Call(ftsDescrip, methodInfo, wphrase);
var ftsExp = Expression.Or(texp, dexp);
// Now get ids from the above fts resultset
// THE ASSIGNMENT BELOW THROWS
var selectExp = Expression.Call(typeof(IEnumerable<MVF>), "Select", new Type[]
{
typeof(long)
},
ftsExp,
Expression.Lambda<Func<MFV, long>>(
ftsID,
ftsParam
)
);
// Now set up MV table reference
ParameterExpression vParam = Expression.Parameter(typeof(MV), "mv");
var mvID = Expression.Property(vParam, "MVID");
var containsInfo = typeof(IEnumerable<long>).GetMethod("Contains", new Type[] { typeof(long) });
// Now combine expression to get those mvs with ids in the result set of fts query
var containsExp = Expression.Call(selectExp, containsInfo, mvID);
return Expression.Lambda<Func<MV, bool>>(containsExp, vParam);
例外情况是:
No generic method 'Select' on type 'System.Collections.Generic.IEnumerable`1[MVF]' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
typeof(IEnumerable<long>)
没有定义 select 方法。 Linq Select 方法是扩展方法,不能通过反射直接看到。定义它们的 class 是 Enumerable
。但是 typeof(Enumerable)
也不起作用,因为该方法是通用的。您必须首先从 class 可枚举中获取通用方法,然后使用 MethodInfo.MakeGenericMethod
创建一个采用长参数的方法。
var method = typeof(Enumerable).GetMethods().First(m => m.Name == "Select" &&
m.GetParameters().Last().ParameterType.GetGenericArguments().Length == 2);
var genericSelectFromLongToLong = method.MakeGenericMethod(new Type[] {typeof(long), typeof(long)});
请注意,检查通用参数而不是参数计数可能会更好。
相关表达式所需的两种方法都是静态泛型扩展方法(最重要的是 static 和 generic) Enumerable
class:
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector
)
public static bool Contains<TSource>(
this IEnumerable<TSource> source,
TSource value
)
"calling" 此类方法最方便的方法是以下 Expression.Call
方法重载:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
Type type
参数是 class 定义被调用方法的类型(在本例中是 typeof(Enumerable)
),Type[] typeArguments
是具有类型的数组泛型类型参数(非泛型方法为空,Select
应为 { typeof(TSource), typeof(TResult) }
,Contains
应为 { typeof(TSource) }
)。
将它应用到您的场景中:
var selectExp = Expression.Call(
typeof(Enumerable),
"Select",
new { typeof(MFV), typeof(long) },
ftsExp,
Expression.Lambda<Func<MFV, long>>(ftsID, ftsParam)
);
和
var containsExp = Expression.Call(
typeof(Enumerable),
"Contains",
new [] { typeof(long) },
selectExp,
mvID
);