Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
505 views
in Technique[技术] by (71.8m points)

entity framework core - How can I prevent EF7 from eagerly fixing up navigation properties?

I have an issue using EF7 in a web application with which I could use some help. I'm currently using EF7 RC1.

Here are some models that illustrate my problem.

Contact

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

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

ContactType

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

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

These models are related via Fluent API like this:

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

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

My needs are to be able to retrieve a collection of Contact entities from the database with their ContactType property loaded. EF makes this quite easy:

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

The issue is that in loading the ContactType properties of the Contact entities (which happens due to the call to .Include() in the query), EF also helpfully loads the Contacts property of each ContactType entity, resulting in an infinite chain of Contacts pointing at ContactTypes and ContactTypes pointing at Contacts. I understand why this is the default behavior and that it's helpful in many cases, but my needs are to serialize these entities to JSON and send them down to the client - it's a read-only situation.

My desired behavior is for EF to return a collection of Contacts with loaded (non-null) ContactType properties that have their Contacts property set to null. Is this something EF can do? Is there any way to end up with the object graph I want short of manually nulling out properties I don't want populated?

Things I've tried:

  • Appending .AsNoTracking() to the EF query (which doesn't seem to stop the Contacts property of the ContactType entity from being loaded)
  • Telling Json.NET not to serialize infinite reference loops (which is required to avoid infinite recursion during serialization, but still results in a lot of extra data being serialized)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can't avoid EF to load ContactType.Contacts collection, as it's not actually loading it but filling the collection with the loaded Contact instances. This is why using AsNoTracking has no efect, because is not a problem of lazy loading nor ChangeTracker.

You have three possible solutions:

  1. Use Json.NET ReferenceLoopHandling = ReferenceLoopHandling.Ignore, but as you stated it will generate lot of unnecesary data, as you will get the collection of Contacts for every ContactType
  2. Use [JsonIgnore] attribute on ContactType.Contacts so it will be ignored by the serializer. But it will ignore it always, I don't know if you need it in other situations
  3. Define a DTO, use something like Automapper to map your data in it (without Contacts collection) and serialize it

I would prefer the 3rd option as I don't like sending domain model objects to the client, and it avoid adding attributes to domain model not related with domain.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...