创建一个为实体框架生成参数查询的表达式树

问题描述 投票:0回答:1

我正在尝试创建一个通用类,用于编写实体框架 (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

如果我取消注释这两行注释并注释上面的两行,则会收到一条错误消息,指出参数“值”未绑定。

我希望我提供了所有有用的细节:)

entity-framework expression-trees
1个回答
15
投票

当参数作为

ConstantExpression
传递时,如下所示:

Expression.Constant(myString)

...它将在结果查询上生成固定的常量符号:

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

如果我们使用像Expression Tree Visualizer这样的工具来分析像

(f => f.Bar == myString))
这样的表达式,我们会发现参数实际上是一个
MemberExpression
。 因此,为了在结果查询中使用参数而不是字符串常量,我们必须传递诸如对象属性之类的东西,或更方便的匿名类型:

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

通过这种方式,我们传递刚刚创建的对象的属性,并且表达式树获得可重用的

MemberExpression
,从而产生针对缓存进行优化的
CommandText

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Bar] AS [Bar], 
FROM [dbo].[Foo] AS [Extent1]
WHERE [Extent1].[Bar] = @p__linq__0
© www.soinside.com 2019 - 2024. All rights reserved.