【问题标题】:How can I prevent EF7 from eagerly fixing up navigation properties?如何防止 EF7 急切地修复导航属性?
【发布时间】:2025-12-03 13:40:01
【问题描述】:

我在 Web 应用程序中使用 EF7 时遇到问题,我需要一些帮助。我目前正在使用 EF7 RC1。

这里有一些模型可以说明我的问题。

联系方式

public class Contact
{
    public Guid Id { get; set; }

    public string Desc { get; set; }
    public ContactType ContactType { get; set; }
}

联系人类型

public class ContactType
{
    public Guid Id { get; set; }
    public string Desc { get; set; }

    public ICollection<Contact> Contacts { get; set; }
}

这些模型通过 Fluent API 关联,如下所示:

modelBuilder.Entity<Contact>(entity => {
     // abridged for clarity

     entity
        .HasOne(c => c.ContactType)
        .WithMany(ct => ct.Contacts)
        .IsRequired();                
});

我的需要是能够从加载了 ContactType 属性的数据库中检索联系人实体的集合。 EF 让这变得非常简单:

using(var context = new MyDbContext()) {
    var contacts = await context
        .Contacts
        .Include(c => c.ContactTypes)
        .Where(/* some search criteria */)
        .ToListAsync();
}

问题在于,在加载 Contact 实体的 ContactType 属性时(由于在查询中调用 .Include() 而发生),EF 还有助于加载每个 ContactType 实体的 Contacts 属性,从而导致无限链指向ContactTypes 的Contacts 和指向Contacts 的ContactTypes。我理解为什么这是默认行为并且它在许多情况下很有帮助,但我需要将这些实体序列化为 JSON 并将它们发送到客户端 - 这是一种只读情况。

我想要的行为是让 EF 返回一组具有已加载(非 null)ContactType 属性的联系人,这些属性的 Contacts 属性设置为 null。这是EF可以做到的吗?有什么方法可以最终得到我想要的对象图,而不是手动清空我不想填充的属性?

我尝试过的事情:

  • 将 .AsNoTracking() 附加到 EF 查询(似乎不会停止 ContactType 实体的 Contacts 属性不会被加载)
  • 告诉 Json.NET 不要序列化无限引用循环(即 需要在序列化期间避免无限递归,但仍然 导致大量额外数据被序列化)

【问题讨论】:

    标签: entity-framework-core


    【解决方案1】:

    您无法避免 EF 加载 ContactType.Contacts 集合,因为它实际上并没有加载它,而是使用加载的 Contact 实例填充集合。 这就是为什么使用 AsNoTracking 没有效果的原因,因为不是延迟加载的问题,也不是 ChangeTracker 的问题。

    您有三种可能的解决方案:

    1. 使用 Json.NET ReferenceLoopHandling = ReferenceLoopHandling.Ignore,但正如您所说,它会生成大量不必要的数据,因为您将获得每个 ContactType 的联系人集合
    2. 在 ContactType.Contacts 上使用 [JsonIgnore] 属性,以便序列化程序忽略它。但是它总是会忽略它,我不知道你在其他情况下是否需要它
    3. 定义一个 DTO,使用 Automapper 之类的东西在其中映射您的数据(不包含联系人集合)并对其进行序列化

    我更喜欢第三个选项,因为我不喜欢将域模型对象发送到客户端,并且它避免向与域无关的域模型添加属性。

    【讨论】:

    • 接受这个作为答案 - 我担心会是这种情况,但我感谢您的帮助。 Automapper 不在我的关注范围内,可能适合我们的需求。
    【解决方案2】:

    我有同样的问题Entity Framework 7 Core disable auto loading

    我添加 AsNoTracking()

    IQueryable<ScheduleModel> q = _db.Schedules;
    q = q.AsNoTracking();
    q = q.Include(x => x.ElementItem);
    q = q.Include(x => x.ScheduleHours);
    

    属性现在不会自动填充。

    【讨论】: