【问题标题】:Create an Expression Tree that generates a parametric query for Entity Framework创建为实体框架生成参数查询的表达式树
【发布时间】:2013-07-10 11:20:38
【问题描述】:

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

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

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

【问题讨论】:

  • 你能解释一下为什么目前的结果对你来说不够好吗?
  • 因为 EF 和 SQL 都不能使用第一个查询来正确缓存它们的输出。当然,EF 会缓存已编译的查询,但除非输入完全相同,否则缓存的项目将不可用。 SQL Server 也会发生同样的情况,它将缓存特定于该值的查询计划。
  • 我遇到了同样的问题。看来您无法通过 Expression.Constant(myValue)。使用Expression Tree Visualizer,您可能会发现在检查实际工作查询时该值作为 MemberExpression 传递,而不是 ConstantExpression,但我仍然无法获取外部变量并使用 Expression.Property 或类似方法传递它。

标签: entity-framework expression-trees


【解决方案1】:

当参数作为ConstantExpression 传递时,像这样:

Expression.Constant(myString)

...它将在结果查询中产生一个固定的、恒定的符号:

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

如果您使用某些工具来分析像 (f =&gt; f.Bar == myString)) 这样的表达式,就像我使用 Expression Tree Visualizer 所做的那样,您会看到参数实际上是 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

【讨论】:

  • 谢谢 :) 我会尽快尝试 :)
猜你喜欢
  • 2016-05-22
  • 2019-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
  • 2014-01-25
  • 1970-01-01
相关资源
最近更新 更多