2013-07-10 63 views
6

我試圖創建一個泛型類來用於組成實體框架(5)的查詢。創建一個表達式樹,爲實體框架生成參數查詢

我得到它的工作,唯一的問題是,該值被注入爲查詢常數而不是作爲參數。這樣可以減少EF緩存查詢並稍後重用它的可能性。

這是我到目前爲止。

public class MinDateFilter<T> : IFilter<T> where T : class 
{ 
    private readonly Expression<Func<T, bool>> _predicate; 

    public MinDateCandidateFilter(Expression<Func<T, DateTime>> propertySelector, DateTime from) 
    { 
     from = from.Date.AddDays(-1); 
     from = new DateTime(from.Year, from.Month, from.Day, 23, 59, 59, 999); 

     Expression value = Expression.Constant(from, typeof(DateTime)); 
     //ParameterExpression variable = Expression.Variable(typeof(DateTime), "value"); 

     MemberExpression memberExpression = (MemberExpression)propertySelector.Body; 
     ParameterExpression parameter = Expression.Parameter(typeof(T), "item"); 
     Expression exp = Expression.MakeMemberAccess(parameter, memberExpression.Member); 

     Expression operation = Expression.GreaterThan(exp, value); 
     //Expression operation = Expression.GreaterThan(exp, variable); 
     _predicate = Expression.Lambda<Func<T, bool>>(operation, parameter); 
    } 

    public IQueryable<T> Filter(IQueryable<T> items) 
    { 
     return items.Where(_predicate); 
    } 
} 

這個類可以通過兩種方式使用:

由子類是:

public class MinCreationDateCandidateFilter : MinDateFilter<Candidate> 
{ 
    public MinCreationDateCandidateFilter(DateTime @from) : base(c => c.CreationDate, @from) {} 
} 

或簡單地通過實例吧:

var filter = new MinDateFilter<Entities.Transition>(t => t.Date, from.Value); 

這是我設法實現到目前爲止:

SELECT 
[Extent1].[Id] AS [Id] 
-- Other fields 
FROM [dbo].[Candidates] AS [Extent1] 
WHERE [Extent1].[CreationDate] > convert(datetime2, '1982-12-09 23:59:59.9990000', 121) 

代替

SELECT 
[Extent1].[Id] AS [Id] 
-- Other fields 
FROM [dbo].[Candidates] AS [Extent1] 
WHERE [Extent1].[CreationDate] > @p__linq__0 

如果我取消這兩個註釋行,我的評論上面的兩個,我得到一個錯誤,指出參數「值」未綁定。

我希望我給所有有用的細節:)

+0

你能解釋一下爲什麼不是當前結果你不夠好? – svick

+2

因爲EF和SQL都不能使用第一個查詢來正確緩存其輸出。 當然,EF會緩存已編譯的查詢,但除非輸入完全相同,否則該緩存項目將不​​可用。 對於將緩存特定於此值的查詢計劃的SQL Server也會發生同樣的情況。 – Kralizek

+0

我遇到同樣的問題。看來你不能傳遞Expression.Constant(myValue)。使用[Expression Tree Visualizer](http://exprtreevisualizer.codeplex.com/)時,您可能會發現在檢查實際工作查詢時,該值是作爲MemberExpression傳遞的,而不是ConstantExpression,但我仍無法獲取外部變量並通過Expression.Property或類似的方式傳遞它。 –

回答

8

當一個參數爲ConstantExpression過去了,就像這樣:

Expression.Constant(myString) 

...這將產生對一個固定的,恆定的符號結果查詢:

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Bar] AS [Bar], 
FROM [dbo].[Foo] AS [Extent1] 
WHERE [Extent1].[Bar] = "Some text" 

如果你使用一些工具來分析像(f => f.Bar == myString))像我一樣用表達式樹可視化表達,你看日在參數實際上是一個MemberExpression。 所以,如果你想在你得到的查詢參數,你必須通過像一個對象的屬性,或者更方便的匿名類型:

Expression.Property(
    Expression.Constant(new { Value = myString }), 
    "Value" 
) 

這樣你要傳遞的只是一個特性-created對象和表達式樹得到了MemberExpression,導致以下CommandText

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Bar] AS [Bar], 
FROM [dbo].[Foo] AS [Extent1] 
WHERE [Extent1].[Bar] = @p__linq__0 
+0

謝謝:)我會嘗試它:) – Kralizek

+3

簡單和工作,謝謝 – pkmiec