Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
like image 805
Jammer Avatar asked Feb 16 '16 17:02

Jammer


2 Answers

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.

like image 171
tede24 Avatar answered Oct 08 '22 20:10

tede24


I have same question Entity Framework 7 Core disable auto loading

I add AsNoTracking()

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

Properties not populate automatic now.

like image 33
Alexandr Sulimov Avatar answered Oct 08 '22 21:10

Alexandr Sulimov