【问题标题】:My ForEach loop with a LINQ expression cannot be translated?无法翻译带有 LINQ 表达式的 ForEach 循环?
【发布时间】:2021-11-26 18:29:26
【问题描述】:

如果过期日期是 2 天或 15 天,我需要检查我所有的化学品并向用户发送警告。 我已经这样设置了,所以警告被发送了两次。到期前 2 周一次,到期前 2 天一次。

所以我有这个循环:

foreach (var chemical in _context.Chemical.Where(c => (c.DateOfExpiration - DateTime.Now).TotalDays == 15 || (c.DateOfExpiration - DateTime.Now).TotalDays == 2))
{
    // send out notices
    var base = _context.Base.Find(chemical.BaseId);
    var catalyst = _context.Catalyst.Find(chemical.CatalystId);

    _warningService.SendSafetyWarning(chemical, base, catalyst.Name);
}

但每当我运行它时,我都会收到此错误:

System.InvalidOperationException: 'The LINQ expression 'DbSet<Chemical>
.Where(p => (p.DateOfExpiration - DateTime.Now).TotalDays == 15 || (p.DateOfExpiration - DateTime.Now).TotalDays == 2)'
could not be translated.

我不确定为什么我运行它时会出现这个错误。

有没有更好的方法来做我想做的事情?

谢谢!

【问题讨论】:

  • EntityFunctions.DiffDays 可能会解决您的问题。
  • 我同意@rb。就目前的代码而言,基本上每次在 for 循环中,DateTime.Now 都会发生变化,这意味着查询会发生变化,这显然不好。
  • 我建议您创建两个要检查的日期,例如 var fifteenDays = DateTime.Today.AddDays(15);,然后您需要将截断的 DateOfExpiration 日期与它们进行比较。根据错误,我认为您使用的是较旧的 EF,除非您仅将日期存储在该列中,否则需要 DbFunctions.TruncateTime(c.DateOfExpiration)
  • @RB。您的意思是在我的foreach 循环中添加这样的内容? where EntityFunctions.DiffDays(DateTime.Now, c.DateOfExpiration) == 2 || EntityFunctions.DiffDays(DateTime.Now, c.DateOfExpiration) == 15
  • @Rb EntityFunctions 是否已弃用?您链接到的问题包含已弃用的 cmets?

标签: c# foreach entity-framework-core


【解决方案1】:

DateTime.TotalDays 返回一个双精度值。

您可能会更幸运地将代码更改为:

foreach (var chemical in _context.Chemical.Where(c => ((int)(c.DateOfExpiration - DateTime.Now).TotalDays == 15) || ((int)(c.DateOfExpiration - DateTime.Now).TotalDays == 2)))
{
    // send out notices
    var base = _context.Base.Find(chemical.BaseId);
    var catalyst = _context.Catalyst.Find(chemical.CatalystId);

    _warningService.SendSafetyWarning(chemical, base, catalyst.Name);
}

【讨论】:

    【解决方案2】:

    这个问题有两种解决方案。

    我将首先解释为什么它会引发错误。 EF 正在将您的代码转换为 SQL Expression。使用 Expression 时,您无法解析任何 C# 代码并期望它能够正常工作。 EF 需要能够将其转换为 SQL。这就是为什么您会收到异常说它无法翻译您的代码。

    当直接在DbSetDbContext 上的Where 子句中工作时,您实际上是在与Where 方法对话以获取IQueryable。其中有一个Expression&lt;Func&lt;bool, T&gt;&gt; 参数,其中T 是您的DbSet&lt;T&gt; 类型。

    您需要简化查询,以便将其转换为 SQL。或者您可以将您的IQueryable 转换为IEnumerable

    当您想在 SQL 服务器上运行查询时,您需要使用第一种方法。但是,当您可以在客户端上运行查询时,您可以使用第二种方法。

    vivek nuna 提到的第一个方法是使用EntityFunctions。它可能不足以满足您的用例,因为它的功能有限。

    DateTime.Now 每次迭代都不同的说法是不正确的。因为查询只转换一次,所以它不会在每次迭代时都运行查询。它根本不知道如何将您的查询转换为 SQL。

    https://docs.microsoft.com/en-us/dotnet/api/system.data.objects.entityfunctions?view=netframework-4.8

    第二种方法是在DbSet 之后添加AsEnumerable。这将从您的 SQL 服务器查询您的所有数据,并在 C# 中进行评估。如果您有大量数据集,通常不建议这样做。示例:

    var chemicals = _context.Chemical.AsEnumerable(); // this will get the complete collection
    
    chemicals.Where(i => i.Value == true); // everything will work now
    

    并回答“更好”的方法。代码更简洁:

    如果您有 BaseCatalyst 的导航属性,您也可以将其包含在查询中。

    请注意,您仍然可以在使用_context.Chemical 上的Where 拉取所有客户端之前查询服务器端,然后包括并调用AsEnumerable

    var chemicals = _context.Chemical.Include(i => i.Base).Include(i => i.Catalyst).AsEnumerable();
    
    foreach (var chemical in chemicals.Where(i => true)) // set correct where clause
    {
        _warningService.SendSafetyWarning(chemical, chemical.Base, chemical.Catalyst);
    }
    

    【讨论】:

      【解决方案3】:

      您可以在之前声明变量,然后将其传递给您的表达式。喜欢DateOfExpiration == nextFifteenDays .....

      var nextFifteenDays = DateTime.Now.Adddays(15);
      
      
      var nextTwoDays = DateTime.Now.AddDays(2);
      

      或者你可以使用EntityFunctions.Diffdays

      @neil 很好地解释了错误的原因,即 DateTime.Now 将不断变化,因此无法使用。

      【讨论】:

      • 所以是我的where 语句中的DateTime.Now 使它出错了?谢谢!
      • 是的@neil 还添加了您的错误原因
      • TotalDays 是你的错误。
      • @SkyeBoniwell 我的错,实际上我不在电脑前。所以无法尝试 tum run。最好创建两个变量,一个为十五天,一个为两天,然后在您的查询中使用。它会工作
      • @SkyeBoniwell, TotalDays 无法转换为 SQL 使用 EntityFunctions.Diffdays
      猜你喜欢
      • 2021-03-01
      • 2021-12-28
      • 1970-01-01
      • 2022-12-03
      • 1970-01-01
      • 2021-11-27
      • 2022-10-20
      • 2020-09-29
      相关资源
      最近更新 更多