【问题标题】:ASP.NET MVC6 "The binary operator Equal is not defined"ASP.NET MVC6“未定义二元运算符 Equal”
【发布时间】:2016-12-21 12:31:03
【问题描述】:

我有这个模型:

[Table("tblDbFile")]
public class DbFile
{
    [Key]
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Engine { get; set; }
    public string Hash { get; set; }
    public long Size { get; set; }
    public DbDir Directory { get; set; }
}

[Table("tblDbDir")]
public class DbDir
{
    [Key]
    public Guid Id { get; set; }
    public string Name { get; set; }
    public DbDir Parent { get; set; }
}

如果我尝试执行 _context.DbFile.Where(n => n.Directory == Dir) 行,我会收到此错误:

Dir 是 DbDir 类型)

消息:

InvalidOperationException:没有为类型“System.Nullable`1[System.Guid]”和“Models.DbDir”定义二元运算符 Equal。

我该如何解决这个问题?使用_context.DbFile.Where(n => n.Directory.Id == Dir.Id) 有效,但这对我来说不是一个选项,因为首先,我必须经常使用这些类型的引用,其次,Dir 可能为空。

我已经试过了:

  • 覆盖对象的 ==!= 运算符,这确实有效,但它不再优化 SQL 查询,而是在 C# 中进行比较。
  • 试图将 Dir 声明为 DbDir?(失败)
  • 使用 n.Directory.Equals(Dir)(与覆盖 == 的问题相同)
  • 在表达式中使用Dir??.Id(失败)

【问题讨论】:

  • 这是哪个版本的 EF Core?旧版本中存在可能导致此异常消息的问题,已修复,但可能仍然存在其他问题。这个特殊的异常消息清楚地指出了 EF 中的一个错误:它抱怨您没有执行的特定比较。 EF 的异常消息应该说明 youre 不支持,或者应该适当地翻译它。如果它在最新版本中仍然存在并且尚不为人所知,则可能值得在他们的问题跟踪器上报告。
  • @hvd 7.0.0-rc1-16348
  • 7.0.* 是旧版本号,这个版本号在 rc1 之后被删除了。当前版本是 1.1.0。见github.com/aspnet/EntityFramework/releases。在 NuGet 上也可以使用 Microsoft.EntityFrameworkCore 而不是 EntityFramework.Core

标签: c# asp.net entity-framework entity-framework-core


【解决方案1】:

正如您所发现的,查询生成器无法翻译您的运算符。你必须使用你不喜欢的版本:

_context.DbFile.Where(n => n.Directory.Id == Dir.Id)

是的,这在null 案例中是有问题的;所以...在这种情况下,您需要以不同的方式编写它:

if(Dir == null) {
    query = _context.DbFile.Where(n => n.Directory == null);
}
else {
    query = _context.DbFile.Where(n => n.Directory.Id == Dir.Id);
}

顺便说一句;如果你也有n.DirectoryId,你可能会发现使用它很方便:

if(Dir == null) {
    query = _context.DbFile.Where(n => n.DirectoryId == null);
}
else {
    var id = Dir.Id;
    query = _context.DbFile.Where(n => n.DirectoryId == id);
}

【讨论】:

  • “这在 null 情况下是有问题的” - 为什么?表达式不只是检查,而不是执行吗?
  • “查询生成器无法翻译您的运算符”——它当然有办法。它可能未实现,也可能实现不正确,但这并非不可能,并且异常消息向我表明 EF 正在尝试实现它。
  • @CodeCaster 实际上,它是一个被拆开的表达式树 - 但是:这并不意味着每个引擎都可以理解每个可能的设置。碰巧的是,空成员的值往往得不到很大的支持,因此是有问题的。尽管公平地说,尚不清楚是否存在对某些内容的有效解释——如果执行——会引发异常。
  • @Marc 无视我之前的评论,我不应该在现有答案下提出新问题。无论如何感谢您的解释。
【解决方案2】:

Dir 为 null 的问题可以通过检查它是否为 null 来轻松解决,并且只有当它不为 null 时才比较 Id,如下所示:

_context.DbFile.Where(n => Dir == null || n.Directory.Id == Dir.Id)

作为替代方案,如果 n.Directory 也可以为 null 并且应该与 Dir == null 匹配,则使用此:

_context.DbFile.Where(n => (Dir == null && n.Directory == null)
                        || (Dir != null && n.Directory.Id == Dir.Id))

【讨论】:

  • 注意:您需要对这种类型的复杂性保持谨慎 - 它会很快导致生成的查询非常不理想。很明显,这很大程度上取决于所使用的工具——我要说的是:确保你看看它正在生成什么。
  • @MarcGravell 我刚刚检查过。查询是N'SELECT [n].[Id], [n].[DirectoryId], [n].[Engine], [n].[Hash], [n].[Name], [n].[OwnerId], [n].[Size] FROM [tblDbFile] AS [n] WHERE (@__p_0 = 1 AND [n].[DirectoryId] IS NULL) OR (@__p_1 = 1 AND ([n].[DirectoryId] = @__Dir_Id_2))',N'@__p_0 bit,@__p_1 bit,@__Dir_Id_2 uniqueidentifier',@__p_0=0,@__p_1=1,@__Dir_Id_2='3D4AF7D3-18CE-48F0-1E75-08D4298C31D6'。当然更复杂,但仍然在 SQL 而不是 C# 中进行比较
  • @AyrA 是的,这种事情会让我彻夜难眠;我对我的 SQL 非常挑剔:)
猜你喜欢
  • 2013-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多