2013-07-10 4 views
6

Ich versuche, eine generische Klasse zum Erstellen von Abfragen für Entity Framework (5) zu erstellen.Erstellen Sie eine Ausdrucksbaumstruktur, die eine parametrische Abfrage für Entity Framework generiert

Ich habe es funktioniert, das einzige Problem ist, dass der Wert als eine Konstante der Abfrage statt als ein Parameter injiziert wird. Dies reduziert die Möglichkeiten für EF, die Abfrage zwischenzuspeichern und später wiederzuverwenden.

Das ist, was ich bis jetzt bekommen habe.

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

diese Klasse kann auf zwei Arten verwendet werden:

von Sub-Klassierung es:

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

oder einfach durch Instanziieren:

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

Dies ist, was ich geschafft, bis jetzt zu erreichen:

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) 

statt

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

Wenn ich Kommentar- der zwei Zeilen kommentiert und ich kommentieren Sie die beiden oben, erhalte ich eine Fehlermeldung, dass der Parameter „Wert“ ist nicht gebunden.

Ich hoffe, ich habe all nützlichen Details :)

+0

Können Sie sich erklären, warum ist nicht das aktuelle Ergebnis gut genug für Sie? – svick

+2

Da EF und SQL die erste Abfrage nicht verwenden können, um ihre Ausgabe ordnungsgemäß zwischenzuspeichern. Natürlich wird EF die kompilierte Abfrage zwischenspeichern, aber das zwischengespeicherte Element wird nicht verwendbar sein, wenn die Eingabe nicht genau gleich ist. Gleiches passiert mit SQL Server, der den für diesen Wert spezifischen Abfrageplan zwischenspeichert. – Kralizek

+0

Ich habe das gleiche Problem. Es scheint, dass Sie Expression.Constant (myValue) nicht übergeben können. Wenn Sie [Expression Tree Visualizer] (http://exprtreevisualizer.codeplex.com/) verwenden, werden Sie möglicherweise feststellen, dass der Wert als MemberExpression übergeben wird, wenn eine tatsächlich aktive Abfrage geprüft wird, nicht ConstantExpression, aber ich konnte immer noch keine externe Variable abrufen und übergeben Sie es mit Expression.Property oder ähnlich. –

Antwort

8

Wenn ein Parameter als ConstantExpression, wie diese geführt wird:

Expression.Constant(myString) 

... es wird ein festes, konstantes Symbol auf der Herstellung von resultierende Abfrage:

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

Wenn Sie ein Tool verwenden, um einen Ausdruck wie (f => f.Bar == myString)) wie ich mit Expression Baum Visualizer tat zu analysieren, sehen Sie th bei dem Parameter ist eigentlich ein MemberExpression. Also, wenn Sie einen Parameter auf Ihre resultierende Abfrage möchten, haben Sie so etwas wie eine Eigenschaft eines Objekts zu übergeben, oder die bequemere anonymen Typ:

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

diese Weise können Sie eine Eigenschaft der gerade vorbei sind -created Objekt und Ihr Ausdrucksbaum werden ein MemberExpression, was in den folgenden CommandText:

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

Danke :) Ich werde es so schnell wie möglich versuchen :) – Kralizek

+3

einfach und funktioniert, danke – pkmiec

Verwandte Themen