使用不在数据库中的实体对象进行LINQ查询-错误:LINQ to Entities无法识别该方法

时间:2019-02-27 23:05:11

标签: c# entity-framework linq iqueryable

我有一个从数据库创建的Entity Framework v5模型。表格Season有一个对应的实体,称为Season。 A screenshot of the Entity Framework EDMX showing the Season, Locations, Project, and Project_Group entities我需要计算一个Project_Group每年该季节的最小开始日期和最大结束日期。然后,我需要能够在其他LINQ查询中加入那些年度最小/最大季节值。为此,我在数据访问层项目中创建了SeasonLimits类。 (数据库中不存在SeasonLimits表 。)

public partial class SeasonLimits : EntityObject
{
    public int Year { get; set; }
    public DateTime Min_Start_Date { get; set; }
    public int Min_Start_Date_ID { get; set; }
    public DateTime Max_End_Date { get; set; }
    public int Max_End_Date_ID { get; set; }

    public static IQueryable<SeasonLimits> QuerySeasonLimits(MyEntities context, int project_Group_ID)
    {
        return context
            .Season
            .Where(s => s.Locations.Project.Project_Group.Any(pg => pg.Project_Group_ID == project_Group_ID))
            .GroupBy(x => x.Year)
            .Select(sl => new SeasonLimits
            {
                Year = sl.Key,
                Min_Start_Date = sl.Min(d => d.Start_Date),
                Min_Start_Date_ID = sl.Min(d => d.Start_Date_ID),
                Max_End_Date = sl.Max(d => d.End_Date),
                Max_End_Date_ID = sl.Max(d => d.End_Date_ID)
            });
    }
}

// MVC Project
var seasonHoursByYear =
    from d in context.AuxiliaryDateHours
    from sl in SeasonLimits.QuerySeasonLimits(context, pg.Project_Group_ID)
    where d.Date_ID >= sl.Min_Start_Date_ID
        && d.Date_ID < sl.Max_End_Date_ID
    group d by new
    {
        d.Year
    } into grp4
    orderby grp4.Key.Year
    select new
    {
        Year = grp4.Key.Year,
        HoursInYear = grp4.Count()
    };

在我的MVC项目中,每当我尝试在LINQ查询JOIN中使用QuerySeasonLimits方法时,都会收到消息,

  

“ LINQ to Entities无法识别该方法   'System.Linq.IQueryable`1 [MyDAL.SeasonLimits]   QuerySeasonLimits(MyDAL.MyEntities,MyDAL.Project_Group)'方法,和   此方法不能转换为商店表达式。”

是否由于SeasonLimits不是数据库中存在的实体而产生此错误?如果无法以这种方式完成此操作,是否还有另一种方法可以引用该逻辑,以便可以在其他LINQ查询中使用它?

2 个答案:

答案 0 :(得分:1)

EF正在尝试将查询转换为SQL,并且由于方法与生成的SQL之间没有直接映射,因此会出现错误。

第一种选择是不使用该方法,而是直接在原始查询中编写该方法的内容(由于目前还没有运行VS,目前不确定是否可以使用该方法)。在这种情况下,您很有可能会遇到性能非常差的非常复杂的SQL。

因此,第二种选择是:不要害怕使用多个查询来获取所需的内容。有时,向数据库发送更简单的查询并继续进行C#代码的修改(聚合,选择等)也很有意义。每当您尝试枚举查询,或者使用(1|subject) + (1|scenario)ToListToDictionaryToArray方法之一或正在使用时,查询都会转换为SQL。 ToLookupFirstFirstOrDefaultSingle调用(有关详细信息,请参见LINQ documentation)。

一个可能解决您的查询(但很可能不是最佳解决方案)的示例是使用以下命令启动查询:

SingleOrDefault

,其余所有继续。这一微小变化具有根本影响:

  1. 通过调用var seasonHoursByYear = from d in context.AuxiliaryDateHours.ToList() [...] ,将立即查询数据库,整个数据库 ToList表将被加载到应用程序中(如果表中的行太多,这将是性能问题)
  2. 调用QuerySeasonLimits方法时将生成第二个查询(您可以/还应该包括一个AuxiliaryDateHours调用)
  3. ToList查询的其余部分:seasonHoursByYear,分组,...将在内存中发生

在这一点上可能还有其他一些不相关的地方。

我还没有真正研究过代码的意图-因为这可能导致进一步的优化-甚至是完全的返工,最终都可以为您带来更多收益...

答案 1 :(得分:0)

我删除了SeasonLimits对象和QuerySeasonLimits方法,并将该方法的内容直接写在原始查询中。

// MVC Project
var seasonLimits =
    from s in context.Season
        .Where(s => s.Locations.Project.Project_Group.Any(pg => pg.Project_Group_ID == Project_Group_ID))
    group s by new
    {
        s.Year
    } into grp
    select new
    {
        grp.Key.Year,
        Min_Start_Date = grp.Min(x => x.Start_Date),
        Min_Start_Date_ID = grp.Min(x => x.Start_Date_ID),
        Max_End_Date = grp.Max(x => x.End_Date),
        Max_End_Date_ID = grp.Max(x => x.End_Date_ID)
    };

var seasonHoursByYear =
    from d in context.AuxiliaryDateHours
    from sl in seasonLimits
    where d.Date_ID >= sl.Min_Start_Date_ID
        && d.Date_ID < sl.Max_End_Date_ID
    group d by new
    {
        d.Year
    } into grp4
    orderby grp4.Key.Year
    select new
    {
        Year = grp4.Key.Year,
        HoursInYear = grp4.Count()
    };