我的带有 LINQ 表达式的 ForEach 循环无法翻译?
My ForEach loop with a LINQ expression cannot be translated?
我需要检查我所有的化学品,并在有效期为 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.
我不确定为什么在我 运行 时它会给我这个错误。
有没有更好的方法来完成我想做的事情?
谢谢!
您可以先声明变量,然后将其传递给您的表达式。喜欢 DateOfExpiration == nextFifteenDays
…..
var nextFifteenDays = DateTime.Now.Adddays(15);
var nextTwoDays = DateTime.Now.AddDays(2);
或者您可以使用 EntityFunctions.Diffdays
@neil 很好地解释了错误的原因,DateTime.Now 会不断变化,因此无法使用。
这个问题有两种解决方法。
我先解释一下为什么会报错。 EF 正在将您的代码转换为 SQL Expression
。使用 Expression
时,您无法在任何 C# 代码中进行解析并期望它能正常工作。 EF 需要能够将其转换为 SQL。这就是为什么您收到异常说明它无法翻译您的代码的原因。
当直接在 DbSet
或 DbContext
的 Where
子句中工作时,您实际上是在与 IQueryable
的 Where
方法对话。其中有一个参数 Expression<Func<bool, T>>
,其中 T
是您的 DbSet<T>
类型。
您要么需要简化查询,以便将其翻译成 SQL。或者您可以将 IQueryable
转换为 IEnumerable
.
当您想运行在SQL服务器上查询时,您需要使用第一种方法。但是,当您可以 运行 在客户端进行查询时,您可以使用第二种方法。
vivek nuna 提到的第一个方法是使用 EntityFunctions
。它可能不足以满足您的用例,因为它的功能有限。
关于 DateTime.Now
在每次迭代中都不同的说法是不正确的。因为查询只转换一次,所以它不会在每次迭代时 运行 查询。它根本不知道如何将您的查询翻译成 SQL.
第二种方法是在 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
并回答“更好”的方法。这是更简洁的代码:
如果您有 Base
和 Catalyst
的导航属性,您也可以将其包含在查询中。
请注意,您仍然可以在使用 _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);
}
DateTime.TotalDays
returns 一个双。
将代码更改为:
可能会更幸运
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 天或 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.
我不确定为什么在我 运行 时它会给我这个错误。
有没有更好的方法来完成我想做的事情?
谢谢!
您可以先声明变量,然后将其传递给您的表达式。喜欢 DateOfExpiration == nextFifteenDays
…..
var nextFifteenDays = DateTime.Now.Adddays(15);
var nextTwoDays = DateTime.Now.AddDays(2);
或者您可以使用 EntityFunctions.Diffdays
@neil 很好地解释了错误的原因,DateTime.Now 会不断变化,因此无法使用。
这个问题有两种解决方法。
我先解释一下为什么会报错。 EF 正在将您的代码转换为 SQL Expression
。使用 Expression
时,您无法在任何 C# 代码中进行解析并期望它能正常工作。 EF 需要能够将其转换为 SQL。这就是为什么您收到异常说明它无法翻译您的代码的原因。
当直接在 DbSet
或 DbContext
的 Where
子句中工作时,您实际上是在与 IQueryable
的 Where
方法对话。其中有一个参数 Expression<Func<bool, T>>
,其中 T
是您的 DbSet<T>
类型。
您要么需要简化查询,以便将其翻译成 SQL。或者您可以将 IQueryable
转换为 IEnumerable
.
当您想运行在SQL服务器上查询时,您需要使用第一种方法。但是,当您可以 运行 在客户端进行查询时,您可以使用第二种方法。
vivek nuna 提到的第一个方法是使用 EntityFunctions
。它可能不足以满足您的用例,因为它的功能有限。
关于 DateTime.Now
在每次迭代中都不同的说法是不正确的。因为查询只转换一次,所以它不会在每次迭代时 运行 查询。它根本不知道如何将您的查询翻译成 SQL.
第二种方法是在 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
并回答“更好”的方法。这是更简洁的代码:
如果您有 Base
和 Catalyst
的导航属性,您也可以将其包含在查询中。
请注意,您仍然可以在使用 _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);
}
DateTime.TotalDays
returns 一个双。
将代码更改为:
可能会更幸运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);
}