Entity Framework include sempre i dati che sono nel contesto anche se non lo chiedo

Sto usando MVC.NET web API, EF con DB prima, e ho il caricamento pigro distriggersto sul mio contesto. EF sta restituendo troppi dati, anche con LazyLoading distriggersto.

Ad esempio, ho utenti con un ruolo. Quando eseguo una query su Utenti e Includi ruolo, la proprietà Role.Users viene automaticamente riempita con i dati poiché gli utenti sono stati caricati nel contesto.

Perché non posso ottenere EF per darmi solo quello che chiedo? O mi sto perdendo qualcosa di grosso qui?

public partial class User { public int UserID { get; set; } public string Title { get; set; } public string Email { get; set; } public int RoleID { get; set; } .... public virtual Role Role { get; set; } } public partial class Role { public int RoleID { get; set; } public string RoleName { get; set; } .... public virtual ICollection Users { get; set; } } return db.Users.Include(u => u.Role); // ^^ user.Role.Users is filled with 1000s of users 

TL; DR: voglio che EF non carichi mai i dati nelle proprietà / raccolte di navigazione a meno che non lo includa direttamente. Durante la serializzazione su JSON, desidero solo ciò che chiedo esplicitamente. Sembra che anche con il caricamento lento, le proprietà di navigazione già nel contesto (cioè di solito “riferimenti circolari”) vengano caricate e restituite.

Il comportamento che stai vedendo si chiama Relationship Fixup e non puoi disabilitarlo.

Se stai caricando utenti con ruoli per serializzarli e inviarli da qualche parte, suppongo che non desideri tenere traccia delle modifiche delle entity framework nel contesto in cui sono state caricate. Pertanto, non è necessario allegarle al contesto e Puoi usare:

 return db.Users.Include(u => u.Role).AsNoTracking(); 

Oppure utilizzare una proiezione in un object specializzato per la serializzazione, come suggerito da @STLRick.

Non voglio che carichi nulla oltre a ciò che dico di includere.

Sembra che tu debba utilizzare Caricamento esplicito . Fondamentalmente, è ansible caricare quadro specifiche come questa:

 context.Include("Roles") 

A mia conoscenza migliore che non dovrebbe includere quadro correlate. Il caricamento lento dovrebbe infatti essere disabilitato e si potrebbe caricare esplicitamente le proprietà di navigazione con Load .

Puoi selezionare solo ciò che ti serve usando Select() .

 var users = _db.Users.Select(x => new { UserID = x.UserID, Title = x.Title, Email = x.Email, RoleID = x.RoleID }).AsEnumerable(); 

Hai ragione che con il caricamento pigro, si ottengono le proprietà di navigazione perché vengono “toccate” dal serializzatore che le fa caricare. Il caricamento lento dovrebbe essere distriggersto se si desidera che le proprietà ritornino come null. Detto questo, “sembra” che una volta che le quadro vengono caricate nel contesto (tramite altre query, ad esempio), verranno elaborate dal serializzatore. Quindi la risposta è di dire al serializzatore di non restituire le proprietà di navigazione. Il modo migliore che ho potuto trovare per farlo è usare DTO (Data Transfer Objects). Ciò ti consente di restituire esattamente i dati che desideri piuttosto che le tue entity framework reali.

Il tuo DTO potrebbe assomigliare a questo:

 public partial class UserDto { public UserDto(user User) { UserID = user.UserID; Title = user.Title; //... and so on } public int UserID { get; set; } public string Title { get; set; } public string Email { get; set; } public int RoleID { get; set; } //exclude the Role navigation property from your DTO } 

… e poi potresti fare qualcosa del genere:

 return db.Users.Include(u => u.Role).Select(user => new UserDto(user)); 

Primo: accendi il Lazy Loading.

Secondo: se si desidera filtrare ciò che si recupera e si restituisce, eseguire un object di reso personalizzato o qualcosa del genere.

 from u in db.Users join r in db.Roles on u.RoleID equals r.RoleID select new { u.UserID, u.Title, u.Email, r.RoleName } 

O qualcosa di simile. Avrai un object di ritorno minimo e il tuo grafico object sarà piccolo.