Creare un albero delle espressioni che generi una query parametrica per Entity Framework

entity-framework expression-trees

Domanda

Sto cercando di creare una classe generica da utilizzare per comporre query per Entity Framework (5).

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 utilizzata in due modi:

sottoclassando:

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

o semplicemente istanzialo:

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

Questo è quello che sono riuscito a ottenere 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 annullo le due righe commentate e commento i due sopra, ricevo un errore che dice che il parametro "valore" non è associato.

Spero di aver dato tutti i dettagli utili :)

Risposta accettata

Quando un parametro viene passato come ConstantExpression , come questo:

Expression.Constant(myString)

... produrrà un simbolo fisso e costante sulla 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 un'espressione come (f => f.Bar == myString)) come ho fatto con Expression Tree Visualizer, si vede che il parametro è in realtà un MemberExpression . Quindi, se vuoi un parametro sulla tua query risultante, devi passare qualcosa come una proprietà di un oggetto, o il tipo anonimo più conveniente:

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

In questo modo si passa una proprietà dell'oggetto appena creato e l'albero dell'espressione ottiene un'espressione MemberExpression , che MemberExpression il seguente CommandText :

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


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché