Erstellen Sie eine Ausdrucksbaumstruktur, die eine parametrische Abfrage für Entity Framework generiert

entity-framework expression-trees

Frage

Ich versuche, eine generische Klasse zu erstellen, die zum Erstellen von Abfragen für Entity Framework (5) verwendet wird.

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 habe ich bis jetzt erreicht.

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:

indem man es untergliedert:

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

oder einfach durch Instantiierung:

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

Das ist mir bisher gelungen:

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)

Anstatt von

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

Wenn ich die beiden kommentierten Zeilen auskommentiere und die beiden obigen kommentiere, bekomme ich eine Fehlermeldung, dass der Parameter "value" nicht gebunden ist.

Ich hoffe ich habe alle nützlichen Details gegeben :)

Akzeptierte Antwort

Wenn ein Parameter als ConstantExpression , wie folgt:

Expression.Constant(myString)

... es erzeugt ein festes, konstantes Symbol auf der resultierenden Abfrage:

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

Wenn Sie mit einem Werkzeug einen Ausdruck wie (f => f.Bar == myString)) wie mit Expression Tree Visualizer analysieren, sehen Sie, dass der Parameter tatsächlich ein MemberExpression . Wenn Sie also einen Parameter für die resultierende Abfrage haben möchten, müssen Sie etwas wie eine Eigenschaft eines Objekts oder den bequemeren anonymen Typ übergeben:

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

Auf diese Weise übergeben Sie eine Eigenschaft des gerade erstellten Objekts und Ihre Ausdrucksbaumstruktur erhält eine MemberExpression , was zu folgendem CommandText :

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


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum