我在構建一個基於LINQ的查詢生成器。使用在運行時解析的類型調用System.Linq.Queryable方法
其中一項功能是能夠將任意的服務器端投影指定爲查詢定義的一部分。例如:
class CustomerSearch : SearchDefinition<Customer>
{
protected override Expression<Func<Customer, object>> GetProjection()
{
return x => new
{
Name = x.Name,
Agent = x.Agent.Code
Sales = x.Orders.Sum(o => o.Amount)
};
}
}
由於用戶必須隨後能夠排序的投影特性(相對於客戶性能),I重新創建表達的Func<Customer,anonymous type>
代替Func<Customer, object>
:
//This is a method on SearchDefinition
IQueryable Transform(IQueryable source)
{
var projection = GetProjection();
var properProjection = Expression.Lambda(projection.Body,
projection.Parameters.Single());
在爲了返回預計的查詢,我希望能夠做到這一點(事實上,它的概念證明幾乎相同):
return Queryable.Select((IQueryable<TRoot>)source, (dynamic)properProjection);
TRoot是SearchDefinition中的類型參數。這將導致以下異常:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for
'System.Linq.Queryable.Select<Customer,object>(System.Linq.IQueryable<Customer>,
System.Linq.Expressions.Expression<System.Func<Customer,object>>)'
has some invalid arguments
at CallSite.Target(Closure , CallSite , Type , IQueryable`1 , Object)
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet]
(CallSite site, T0 arg0, T1 arg1, T2 arg2)
at SearchDefinition`1.Transform(IQueryable source) in ...
如果你仔細觀察,它錯誤地推斷泛型參數:Customer,object
代替Customer,anonymous type
,這是實際類型的properProjection
表達(雙重檢查)
我解決方法是使用反射。但隨着通用的參數,它是一個真正的混亂:
var genericSelectMethod = typeof(Queryable).GetMethods().Single(
x => x.Name == "Select" &&
x.GetParameters()[1].ParameterType.GetGenericArguments()[0]
.GetGenericArguments().Length == 2);
var selectMethod = genericSelectMethod.MakeGenericMethod(source.ElementType,
projectionBody.Type);
return (IQueryable)selectMethod.Invoke(null, new object[]{ source, projection });
有誰知道一個更好的辦法?
更新:爲什麼dynamic
失敗的原因是匿名類型被定義爲internal
。這就是爲什麼它使用概念驗證項目進行工作,其中所有內容都在同一個程序集中。
我很酷。我仍然想找到一個更清晰的方式來找到正確的Queryable.Select
過載。
是調用'ParameterRebinder.ReplaceParameter'真的有必要?表達式體已經有了正確的類型,所以當表達式被重建時,它將具有正確的類型。我自己的測試似乎在這裏工作。 – 2011-03-25 23:39:43
@JeffM:在匿名類型初始化程序中調用是必要的,以取代原始lambda表達式的參數,否則您會從範圍''引用類型爲'Customer'的變量'x',但未定義' 。我應該創建一個完整的測試用例,因爲它也適用於我的概念驗證項目。 – 2011-03-25 23:44:42
哦,我忘了你使用了一個不同的參數實例來重建你的表達式。我的測試只是重新使用新的lambda表達式(和工程)現有的參數和正文。會爲你做同樣的工作嗎? – 2011-03-25 23:49:17