【问题标题】:Entity Framework use CompiledQuery but allow runtime filter values实体框架使用 CompiledQuery 但允许运行时过滤器值
【发布时间】:2011-05-12 05:47:20
【问题描述】:

我正在尝试使用实体框架重构繁琐的 LINQ-to-SQL 数据层。模型背后的数据库架构很大,一个典型的查询可能有 20 到 30 个包含。 EF 会为此类查询生成大量 SQL 语句,迄今为止最大的是 4k 行,但它们仍然可以及时执行,所以这不是问题。

问题在于 EF 需要很长时间,最多 4 或 5 秒来生成查询。为了克服这个问题,我使用了 CompileQuery。那么问题是现有的 L2S 数据层有很多过滤器可以根据用户输入应用于查询。这些过滤器中的单个值需要在运行时设置。

下面的代码不起作用,因为初始静态值被编译到查询中,但它演示了我正在尝试做的事情。

public static class DataLayer
{
    static Func<MyEntities, int, IQueryable<Prescription>> compiledQuery;

    static int? FilterHpID;
    static Expression<Func<Prescription, bool>> filter1 = x => (FilterHpID == null || x.Prescriber.HPID == FilterHpID);

    static DateTime? FilterDateTime;
    static Expression<Func<Prescription, bool>> filter2 = x => (FilterDateTime == null || x.DateTimeDispensed > FilterDateTime);

    public static List<Prescription> Get(int patientID, int? hpID, DateTime? dispensed)
    {
        FilterHpID = hpID;
        FilterDateTime = dispensed;

        if (compiledQuery == null)
        {
            compiledQuery = System.Data.Objects.CompiledQuery.Compile((MyEntities entities, int id) =>
                        (from pre in entities.Prescription
                         where pre.PatientID == id
                         select pre)
                         .Where(filter1)
                         .Where(filter2));
        }

        using (MyEntities entities = new MyEntities())
        {
            return compiledQuery(entities, patientID).ToList();
        }
    }
}

有什么方法可以在编译后的查询中包含我的过滤器表达式,并且能够在执行查询时设置过滤器表达式的值?

【问题讨论】:

    标签: entity-framework-4.1 linq-expressions compiled-query


    【解决方案1】:

    过滤器必须是编译查询的一部分,之后您可以在调用查询时设置它们。我认为你可以使用类似的东西:

    public static IQueryable<Prescription> Filter1(this IQueryale<Prescription> query, 
        DateTime? param)
    {
        return query.Where(x => (param == null || x.Prescriber.HPID == param));
    }
    

    那么您应该能够将编译后的查询定义为:

     compiledQuery = System.Data.Objects.CompiledQuery
                           .Compile((MyEntities entities, int id, DateTime? param) =>
                               (from pre in entities.Prescription
                                where pre.PatientID == id
                                select pre)
                               .Filter1(param));
    

    它适用于普通查询,但我从未在编译查询中尝试过。如果它不起作用,您必须将过滤器表达式直接放在已编译的查询中:

     compiledQuery = System.Data.Objects.CompiledQuery
                           .Compile((MyEntities entities, int id, DateTime? param) =>
                               (from pre in entities.Prescription
                                where pre.PatientID == id
                                select pre)
                               .Where(x => (param == null || x.Prescriber.HPID == param));
    

    【讨论】:

    • LINQ to Entities 不喜欢它:(。“...无法识别方法 Filter1 方法,并且该方法无法翻译成商店表达式。”。
    • 这将是理想的方式,但不幸的是,在这种情况下,过滤器已经定义在一个单独的类中,并且有很多过滤器需要在不同的查询中重复使用。该方法适用于 L2S,但不适用于 EF,因为现在需要编译查询以使性能可接受。
    • 如果不编译查询,能否只测试扩展方法是否有效?
    • 是的,如果查询未编译,扩展方法可以正常工作。
    • 我有点担心编译查询不支持可重用块,如果它们不支持与非编译查询一起使用的扩展方法。
    【解决方案2】:

    在高低搜索之后,我得出的结论是无法在编译查询中使用可重用的表达式,更不用说需要参数的表达式了。

    以下代码的意图不可能以任何形式或方式实现。

    static Expression<Func<Prescription, bool>> filter1 = x => (FilterHpID == null || x.Prescriber.HPID == 1);
    
    compiledQuery = System.Data.Objects.CompiledQuery.Compile((MyEntities entities, int id) =>
                        (from pre in entities.Prescription
                         where pre.PatientID == id
                         select pre)
                         .Where(filter1));
    

    我们将开始研究使用数据库视图作为解决首先需要使用已编译查询的一种方法。如果不必编译 L2E 查询以避免延迟近 2 秒,则可以添加可重用的表达式过滤器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 2019-04-14
      • 1970-01-01
      • 2017-11-08
      • 2016-08-03
      • 2015-12-29
      • 1970-01-01
      相关资源
      最近更新 更多