2013-07-10 12 views
6

Sto tentando di creare una classe generica da utilizzare per comporre query per Entity Framework (5).Creare un albero delle espressioni che genera una query parametrica per Entity Framework

Ho capito che funziona, l'unico problema è che il valore viene iniettato come costante della query anziché come parametro. Ciò riduce le possibilità per EF di memorizzare nella cache la query e riutilizzarla in seguito.

Questo è quello che ho ottenuto finora.

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); 
    } 
} 

questa classe può essere utilizzato in due modi:

da sub-classing esso:

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

o semplicemente istanziando esso:

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

Questo è ciò che riuscito a raggiungere finora:

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) 

invece di

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

Se io rimuovere il commento le due righe di commento e io commento le due di cui sopra, ottengo un errore che dice che il parametro "valore" non è vincolata.

spero ho dato tutti i dettagli utili :)

+0

Ci può spiegare il motivo per cui non è il risultato corrente abbastanza buono per la vostra? – svick

+2

Poiché sia ​​EF che SQL non possono utilizzare la prima query per memorizzare correttamente l'output. Ovviamente EF memorizzerà nella cache la query compilata ma quell'elemento memorizzato nella cache non sarà utilizzabile a meno che l'input sia esattamente lo stesso. Lo stesso accade a SQL Server che memorizzerà nella cache il piano di query specifico per questo valore. – Kralizek

+0

Sto avendo lo stesso problema. Sembra che tu non possa superare Expression.Constant (myValue). Usando [Expression Tree Visualizer] (http://exprtreevisualizer.codeplex.com/) potresti scoprire che il valore è passato come MemberExpression durante l'ispezione di una query di lavoro effettiva, non di ConstantExpression, ma non riesco ancora a prendere una variabile esterna e passalo con Expression.Property o simili. –

risposta

8

Quando un parametro viene passato come ConstantExpression, come questo:

Expression.Constant(myString) 

... produrrà un simbolo costante fissata sul query risultante:

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

Se si utilizza qualche strumento per analizzare l'espressione come (f => f.Bar == myString)) come ho fatto io con Expression Albero Visualizer, si vede ° al parametro è in realtà un MemberExpression. Quindi, se si vuole un parametro sulla vostra query risultante, è necessario passare qualcosa di simile a una proprietà di un oggetto, o il più conveniente tipo anonimo:

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

In questo modo si sta passando una proprietà del giusto oggetto -Creato e il vostro albero di espressione diventa un MemberExpression, causando la seguente CommandText:

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

Grazie :) Lo proverò APPENA POSSIBILE :) – Kralizek

+3

semplice e funzionante, grazie – pkmiec

Problemi correlati