【问题标题】:Entity Framework calling MAX on null on Records实体框架在记录上调用 MAX 为空
【发布时间】:2010-07-18 03:25:44
【问题描述】:

在 IQueryable 上调用 Max() 并且记录为零时,我得到以下异常。

转换为值类型“Int32”失败,因为具体化值为空。 结果类型的泛型参数或查询必须使用可为空的类型。

var version = ctx.Entries
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
    .Max(e => e.Version);

现在我明白为什么会发生这种情况了,我的问题是,如果表格可以为空,最好的方法是什么。下面的代码可以工作并解决了这个问题,但是很丑的是没有 MaxOrDefault() 的概念吗?

int? version = ctx.Entries
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
    .Select(e => (int?)e.Version)
    .Max();

【问题讨论】:

  • 您使用的是哪个版本的 EF?
  • Microsoft ADO.NET 实体框架功能社区技术预览版 4

标签: entity-framework


【解决方案1】:

是的,在 LINQ to Entities 查询中处理问题的推荐方法是强制转换为 T 的 Nullable。拥有一个具有正确签名的 MaxOrDefault() 方法听起来是一个有趣的想法,但您只需要为每个出现此问题的方法添加一个附加版本,这不会很好地扩展。

这是 CLR 中的工作方式与数据库服务器上的实际工作方式之间的许多不匹配之一。 Max() 方法的签名是这样定义的,因为结果类型应该与 CLR 上的输入类型完全相同。但在数据库服务器上,结果可能为空。出于这个原因,您需要将输入(尽管取决于您编写查询的方式,可能足以将输出)转换为 T 的 Nullable。

这是一个看起来比您上面的解决方案稍微简单的解决方案:

var version = ctx.Entries 
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId) 
    .Max(e =>(int?)e.Version);

希望这会有所帮助。

【讨论】:

  • 嘿,Divega,看看我的回答,你觉得这种方法有什么问题吗?
  • bleevo,恐怕除非您将方法更改为采用 IQueryable 和 Expression> 参数,否则该方法将隐式切换到使用 LINQ to Objects 的客户端评估。就个人而言,我认为只是投射到 T?更简单。
【解决方案2】:

试试这个为你的最大值创建一个默认值。

int version = ctx.Entries 
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId) 
    .Max(e =>(int?)e.Version) ?? 0;

【讨论】:

  • 接受的答案返回一个可为空的 int。您的回答是返回不可为空的 int 的简洁方法,这对我更有帮助。
  • 哇,你的回答是最好的,它对我有用,真的特别感谢。这是我的使用方法:int MaxID = db.Employees.Max(emp => (int?)emp.EmpID) ?? 0;
【解决方案3】:

你可以像这样写一个简单的扩展方法,如果不存在记录,它返回类型 T 的默认值,然后应用 Max 到那个或如果记录存在的查询。

public static T MaxOrEmpty<T>(this IQueryable<T> query)
{
    return query.DefaultIfEmpty().Max();
}

你可以这样使用它

maxId = context.Competition.Select(x=>x.CompetitionId).MaxOrEmpty();

【讨论】:

  • 这个解决方案的好处是您可以指定一个默认值 (DefaultIfEmpty(value))。所以你不必返回一个可为空的类型。
  • 这应该是首选解决方案
【解决方案4】:

我不能拒绝回答:) 我已经测试了以下内容并且它有效,我还没有检查生成的 SQL,所以要小心,一旦我测试了更多,我会更新它。

var test = ctx.Entries
    .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
    .MaxOrDefault(x => x.Version);

public static TResult? MaxOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    where TResult : struct
{
    return source
        .Select(selector)
        .Cast<TResult?>()
        .Max();
}

【讨论】:

  • 这不是 L2E 查询。是L2O。没关系,但不是你问的。 :)
【解决方案5】:

试试这个:

IEnumerable<AlertsResultset> alerts = null;

    alerts = (from POA in SDSEntities.Context.SDS_PRODUCT_ORDER_ALERT
              join A in SDSEntities.Context.SDS_ALERT on POA.ALERT_ID equals A.ALERT_ID
              orderby POA.DATE_ADDED descending
              select new AlertsResultset
              {
                  ID = POA.PRODUCT_ORDER_ALERT_ID == null ? 0:POA.PRODUCT_ORDER_ALERT_ID ,
                  ITEM_ID = POA.ORDER_ID.HasValue ? POA.ORDER_ID.Value : POA.PRODUCT_ID.Value,
                  Date = POA.DATE_ADDED.Value,
                  orderType = SDSEntities.Context.SDS_ORDER.Where(o => o.ORDER_ID == POA.ORDER_ID.Value).FirstOrDefault().ORDER_TYPE,
                  TransactionNumber = POA.PRODUCT_ID.HasValue ? (SDSEntities.Context.SDS_PRODUCT.Where(p => p.PRODUCT_ID == POA.PRODUCT_ID.Value).FirstOrDefault().TRANSACTION_NUMBER) : (SDSEntities.Context.SDS_ORDER.Where(o => o.ORDER_ID == POA.ORDER_ID.Value).FirstOrDefault().TRANSACTION_NUMBER),
                  Publisher = POA.PRODUCT_ID.HasValue ?
                  (
                  SDSEntities.Context.SDS_PRODUCT.Where(p => p.PRODUCT_ID == POA.PRODUCT_ID.Value).FirstOrDefault().PRODUCT_TYPE_NUMBER == "ISSUE" ? (from prod in SDSEntities.Context.SDS_PRODUCT
                                                                                                                                                      join ji in SDSEntities.Context.SDS_JOURNAL_ISSUE on prod.PRODUCT_ID equals ji.PRODUCT_ID
                                                                                                                                                      join j in SDSEntities.Context.SDS_JOURNAL on ji.JOURNAL_ID equals j.JOURNAL_ID
                                                                                                                                                      where prod.PRODUCT_ID == POA.PRODUCT_ID
                                                                                                                                                      select new { j.PUBLISHER_NAME }).FirstOrDefault().PUBLISHER_NAME : (from prod in SDSEntities.Context.SDS_PRODUCT
                                                                                                                                                                                                                          join bi in SDSEntities.Context.SDS_BOOK_INSTANCE on prod.PRODUCT_ID equals bi.PRODUCT_ID
                                                                                                                                                                                                                          join b in SDSEntities.Context.SDS_BOOK on bi.BOOK_ID equals b.BOOK_ID
                                                                                                                                                                                                                          where prod.PRODUCT_ID == POA.PRODUCT_ID
                                                                                                                                                                                                                          select new { b.PUBLISHER_NAME }).FirstOrDefault().PUBLISHER_NAME
                  )
                  : (SDSEntities.Context.SDS_ORDER.Where(o => o.ORDER_ID == POA.ORDER_ID.Value).FirstOrDefault().PUBLISHER_NAME),
                  Alert = A.ALERT_NAME,
                  AlertType = A.ALERT_TYPE,
                  IsFlagged = POA.IS_FLAGGED.Value,
                  Status = POA.ALERT_STATUS
              });

【讨论】:

    【解决方案6】:

    怎么样

    var version = ctx.Entries
        .Where(e => e.Competition.CompetitionId == storeCompetition.CompetitionId)
        .Max(e => (int?)e.Version);
    

    不那么丑,更优雅

    【讨论】:

      【解决方案7】:

      我想建议从现有答案中合并:

      @divega 答案效果很好,sql 输出也很好,但是因为 'don't repeat y我们自己' 扩展将是一种更好的方式,例如 @Code 唯一显示。但是这个解决方案可以根据需要输出更复杂的 sql。 但是您可以使用以下扩展将两者结合在一起:

       public static int MaxOrZero<TSource>(this IQueryable<TSource> source, 
       Expression<Func<TSource, int>> selector)
              {
                  var converted = Expression.Convert(selector.Body, typeof(int?));
                  var typed = Expression.Lambda<Func<TSource, int?>>(converted, selector.Parameters);
      
                  return source.Max(typed) ?? 0;
              }
      

      【讨论】:

        【解决方案8】:

        你可以使用:

        FromSqlRaw("Select ifnull(max(columnname),0) as Value from tableName");
        

        【讨论】:

        • 这不是答案。这是一种解决方法。变通方法的建议常常让人感到沮丧,因为它们没有阐明手头的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-09
        相关资源
        最近更新 更多