Why is EF eagerly loading all navigation properties with EF Database first? - c#

I use EF Database first, because I like to design my databases in SQL Management Studio, and it's quite frankly very easy to make Visual Studio create all the entities directly from the database, without having to do any code.
How ever, I noticed on SQL Profiler, that EF is eagerly loading all the related entities of an object, whenever I call ToList() on the result collection.
Let's say I have an entity like this:
public class SomeEntity
{
public string Name { get; set; }
public IEnumerable<SomeOtherEntity> ListOfOtherEntites { get; set; }
}
I could then have a query, getting a list of these entities:
public IEnumerable<SomeEntity> GetAllOfTheSomeEntities(Guid customerId){
return dbContex.SomeEntity.Where(x => x.CustomerId == customerId);
}
At a later point in the code, I want to do stuff with this list (for instance in a controller in MVC), and I would call ToList() on the query:
var list = repository.GetAllOfTheSomeEntities(customerId).ToList();
Even though I never use the property "ListOfOtherEntites" on the Entity, it's still being loaded into my server's memory.
I know that I don't have to worry about this in Code first, because I can control the loading with the "virtual/non-virtual" properties and Include - but how am I to go about this in Database first?
I could alter my entities after I load them from the database; but they're just gonna get recreated if I update my model at a later point.

You need to mark your navigation properties as virtual to enable lazy loading in entity framework.
public class SomeEntity
{
public string Name { get; set; }
public virtual IEnumerable<SomeOtherEntity> ListOfOtherEntites { get; set; }
}
Doesn't simply modifying the generated code fix the problem?

If you're experiencing issues with lazy loading then I might recommend turning lazy loading off within your EF context.
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
From here you can then include the related entities using LINQ's .Include() method like so:
var posts = context.Blogs.Include(b => b.Posts).ToList();

Related

Entity Framework 7 Core disable auto loading [duplicate]

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)
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:
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
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
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.
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.

Entity Framework code-first lazy loading of navigation properties

I have an Entity Framework code-first project, where one my objects has:
public virtual List<Transaction> Transactions { get; private set; }
It will of course be lazy-loaded, which is what I want. Now, on the same object, I want to introduce a 'convenience property', which does some filtering for me:
public virtual List<Transaction> EnabledTransactions
{
get
{
// TODO: .AsQueryable<Transaction>(); ?
return this.Transactions.Where(t => !t.Disabled).ToList();
}
}
Questions:
Does this new property have to be virtual or not? If it's not virtual, will .NET try to load it and hence negate the lazy loading?
Since none of them is IQueryable (I understand we can't have it as navigation property), does it mean that anyone doing account.Transactions.Where(t => t.Date <= someDate) would force a complete eager load of the whole collection, instead of just getting the items that satisfy the predicate?

Reading related data from a member method of the model

I have a model that has some related data in navigation properties, like so:
public class Document
{
[Key]
public int DocumentId { get; set; }
public string DocumentName { get; set; }
public virtual ICollection<DocumentBeneficiary> DocumentBeneficiaries { get; set; }
public virtual ICollection<DocumentExecutor> DocumentExecutors { get; set; }
public virtual ICollection<DocumentSuccessor> DocumentSuccessors { get; set; }
}
I understand how to do eager loading of this related data from a controller method, like so:
var doc = context.Documents.Include(x => x.DocumentBeneficiaries)
However, what I want to do is write a member method inside the model that takes the data related to an entity, does some processing, and outputs a string. Something like this:
public class Document
{
...
public string ProcessStuff() {
//use data in navigation properties here like so:
foreach (var d in DocumentBeneficiaries) { ... }
}
}
Is this allowable? I can't seem to find anything about it on google. Will it load the related data lazy vs. eager depending on how I load the entity in the controller prior to calling the method in the model?
I realize that some schools of thought hold that models should have no methods, but others say it's ok to put business logic in the model. If I have to I suppose I can make this a controller method, but this way makes more sense to my design if possible. Sorry if this is a somewhat speculative question, but I can't seem to find any info on this pattern.
Yes it will load the DocumentBeneficiaries when you invoke ProcessStuff method as long as Lazyloading is enabled, thou it may not be a good design (my opinion) to add business logic directly into the model, but as you stated, there are some who like it and some who don't.
If you don't load the child collection ahead of time using Include and Lazyloading is enabled, then you will end up making extra database trips while executing ProcessStuff(). Using Include pre loads the data you need with less number of database round trips. It is always better to make less database trips whenever possible.
If Lazyloading is disabled, you have to use Include before invoking ProcessStuff()

Entity framework 'include' including more than it should

I have a class Program:
[Table("Program")]
public class Program
{
[Key]
public long ProgramId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Activity> Activities { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<TimeEntry> TimeEntries { get; set; }
}
The typical usage pattern is to grab a Program and all it's associated Activities. Much less often need Programs and their related Users or TimeEntries.
So, to return json of all the programs and their related activities. I used .Include:
//...
var programActivities = db.Programs.Include(p => p.Activities);
As expected the json emitted has programs and nested activities. Good.
But, it also returned each Program's associated TimeEntries and Users. Didn't expect that! Other articles i've read indicate that you use one .Include for each of the related objects to be returned, so it would follow that if you don't use an .include for an object, you don't get it.
Is there an additional switch or option i have to use to exclude the other related objects?
Don't declare your navigation properties as virtual or disable Lazy Loading behavior. Lazy loading is enable by default and is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook. So, if you want to work with JSON I recommend you turn off lazy loading:
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Now you can load the related entities you want using the Include extension method as part of a query. This behavior is called Eager Loading.
These links can help you to understand better what I explain in my answer:
Loading Related Entities (read the lazy loading section)
Requirements for Creating POCO Proxies
In Entity Framework, when using the Include statement like so db.Programs.Include(p => p.Activities), you are basically ensuring that the navigation property will be eagerly loaded, rather than lazy loaded. Essentially, this means using one database query to get the data for a Program and its related Activites rather than two.
The problem is, what if lazy loading is enabled for the context, any attempt to get the rest of the navigation properties will result in extra database calls. This is probably what's happening when the JSON serializer tries to serialize your Program objects.
In order to prevent this, you should probably look for a way to exclude the navigation properties from being serialized and so they will never be loaded from the database. For example:
[JsonIgnore]
public virtual ICollection<User> Users { get; set; }
[JsonIgnore]
public virtual ICollection<TimeEntry> TimeEntries { get; set; }
This will allow you to keep lazy loading on, while preventing the loading of the rest of the properties. It all depends on your application design, for example if you need to use lazy loading or if your navigation properties need to be exposed occasionally.

Code First and data loading

I should make a choice between EF code first or model first.
I'm not sure what is the best solution for development efficiency and simplest solution for IoC framework integration.
I have tried to start with code first but I could not make the navigation properties to be used as IQueryable. When I declare navigation properties I use ICollection like:
public class Invoice {
...
public ICollection<LineItem> LineItems { get; set; }
...
}
and
public class LineItem {
...
public Invoice Invoice { get; set; }
...
}
When I load Invoice, my LineItems collection is empty because of lazy load.
But when I load this property from database will I load all LineItems of this Invoice from database before I could use this Collection as IQueryable?
My idea is to use IQueryable to make database loading as minimal as possible to increase performance.
What I'm doing wrong? What is the best practice?
When I use Database first I can use my navigation properties as IQueryable out of the box without writing any proxy classes or wrappers.
Thank you
You need to make all of your entity properties virtual. This allows EF to generate a proxy class for your entity, such that you can query over it.
public class Invoice {
public virtual ICollection<LineItem> LineItems { get; set; }
}
You need Virtual Keyword to make Lazyloading working.

Categories

Resources