【问题标题】:Entity Framework Core - orphaned recordEntity Framework Core - 孤立记录
【发布时间】:2016-02-11 07:42:01
【问题描述】:

我在一个应用程序中使用 Entity Framework Core (7.0.0-rc1-final),该应用程序有一些我无法控制的表(它们来自带有孤立记录的数据馈送)。以下是我创建的一些简单模型来说明这个问题:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int BuildingId { get; set; }
    public virtual Building Building { get; set; }
}

public class Building
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Person> People { get; set; }
}

当我尝试查询一个人所在的建筑物的名称并且记录有一个 BuildingId,而 Building 表中没有与之相关的记录时,会引发错误。

这是我计划如何检查孤立记录的示例:

var person = _dbContext.Person
    .AsNoTracking()
    .Where(p => p.Id == personId)
    .Select(p => new
    {
        Name = p.Name,
        HasBuildingName = p.Building == null,
    })
.FirstOrDefault();

这是我在运行上述语句时遇到的异常:

二元运算符 Equal 没有为类型“System.Int32”定义 和“System.Object”。

理想情况下,如果找不到建筑物 ID,我想运行以下代码并让“BuildingName”为空:

   var person = _dbContext.Person
        .AsNoTracking()
        .Where(p => p.Id == personId)
        .Select(p => new
        {
            Name = p.Name,
            BuildingName = p.Building == null ? null : p.Building.Name
        })
        .FirstOrDefault();

这是一个 EF 错误还是有其他方法可以做到这一点?

编辑:

为了清楚起见,在我遇到孤立记录的问题之前,这一切正常:

var person = _dbContext.Person
            .AsNoTracking()
            .Where(p => p.Id == personId)
            .Select(p => new
            {
                Name = p.Name,
                BuildingName = p.Building.Name
            })
            .FirstOrDefault();

【问题讨论】:

  • 可以改为personIdpersonId 的类型是什么?它来自哪里?
  • 如果我只是从 person 对象中检索属性,这工作得很好,或者如果我检索 p.Building.Name 它在记录存在时工作得很好。仅当此人的 BuildingId 在 Building 表中不存在时,这才是问题。
  • 所以你的意思是你已经打破了 fk 约束?还是没有在数据库中定义 fk 约束?您的 buildingid 不为 null 但有值?即使在以前的 EF 版本中,这也不起作用。
  • 不幸的是,我们不控制数据,并且数据库中没有外键约束,如果在 person 表上找不到该对象,我想测试它是否为 null .

标签: c# ef-code-first entity-framework-core


【解决方案1】:

EF 无法处理您有一个非空 Id 指向无处作为导航属性的情况。导航属性由 FK 支持,可选关系由 null Id 表示。

如果您的数据库中没有 fk 约束,我会将这两个实体实现为不相关,并使用 Join 运算符而不是导航属性来查询它们。

var person = _dbContext.Person
            .AsNoTracking()
            .Where(p => p.Id == personId)
            .GroupJoin(_dbContext.Building, x=>x.BuildingId, x=>X.Id,(p,bs)=> new
            {
                Name = p.Name,
                BuildingName = bs.Select(b=>b.Name).FirstOrDefault()
            })
            .FirstOrDefault();

【讨论】:

  • 先用 .Join 尝试过,但无济于事(它将作为内部连接排除),然后看到您添加了 GroupJoin 示例。这成功了,但我对需要在多个关系上执行此操作并不感到兴奋。糟糕的数据永远不会有趣 :( 感谢您帮助我找到可能的解决方案。
  • 尽管这可行,但我对其进行了一些性能测试,并且不采用此解决方案,因为将其分解为 2 个查询要快得多。这当然会因每个项目而异,这仍然回答了我最初的问题。值得一提的是,这个答案的性能可能会有所不同,供人们考虑。
  • 您遇到了什么性能问题?我会避免使用 GroupJoin 并使用常规的内部连接,这样会做得更好,但是由于您的预期结果是我为 GroupJoin 去的建筑物的人和可选的
  • 我基本上运行了您提供的内容,但我的实际模型多次使用不同的输入值(具有有效 id 和无效 id 的值)。我将其与将其分解为单独的查询进行比较,以多次获取具有相同值的第二个模型的值。这也在具有真实数据的完整测试数据库上,因此可能仅适用于我的场景。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 2022-12-31
  • 1970-01-01
  • 2018-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多