Entity Framework code-first lazy loading of navigation properties - c#

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?

Related

why ef lost relationship once SaveChanges?

If I simply do this:
var medical = ctx.Medicals.FirstOrDefault(p => p.ID == medicalViewModel.ID);
var sizeClinics = medical.Clinics.Count;
The amount is (for example) 10 (i.e. I have 10 clinics for that medical).
Now, if I do this:
var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();
medical = ctx.Medicals.FirstOrDefault(p => p.ID == medicalViewModel.ID);
var sizeClinics = medical.Clinics.Count;
The size is 0. Why? It seems it remove relationship after SaveChanges?
Here's the Medicals object:
public partial class Medicals
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Medicals()
{
this.Activities = new HashSet<Activities>();
this.MedicalsRefunds = new HashSet<MedicalsRefunds>();
this.Clinics = new HashSet<Clinics>();
}
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Activities> Activities { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MedicalsRefunds> MedicalsRefunds { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Clinics> Clinics { get; set; }
}
I thing I've noticed: if I analyze medical object with QuickWatch the first time (without SaveChanges part) its as {System.Data.Entity.DynamicProxies.Medicals_650D310387E78A83885649345ED0FB2870EC304BF647B59321DFA0E4FBC78047}.
Instead, if I do SaveChanges and then I retrieve that medical, it is as {MyNamespace.Models.Medicals}.
What can it be?
This question is answered by understanding how Entity Framework works internally. I'll try to highlight the key features here.
Change tracking
Entity Framework has a sort of cache of entities in-memory, called the change tracker.
In your first example, when you fetch an entity from the database:
var medical = ctx.Medicals.FirstOrDefault(p => p.ID == medicalViewModel.ID);
Entity Framework creates the Medicals instance that you receive. When it does so, it also uses that opportunity to store a reference to that object, for its own reasons. It will keep an eye on those objects and track any changes made to them.
For example, if you now call ctx.SaveChanges(); at any point, it's going to look at everything in its change tracker, see which things have been changed, and update those in the database.
There are several benefits attached to this: you don't have to explicitly tell EF that you made changes to some of the entities it was already tracking in its cache, and EF can also spot which specific fields have changed, so it only has to update those specific fields and it can ignore the unchanged fields.
Update from comments: EF only allows the tracking of one instance of a given entity, based on the PK value. So if you've already tracked the Medical with ID 123, you can't track another instance of the same Medical entity with ID 123.
Lazy loading
The code you use suggests that you are lazy loading. I'm going to gloss over the intricate details here, to keep it simple. If you don't know what lazy/eager loading is, I suggest you look this up, as the explanation is too long to write down here. Lazy/eager loading is a key concept in Entity Framework for dealing with entity relations and how to fetch related entities.
When dealing with lazy loading, EF slightly tinkers with your entity when it fetches it for you. It puts a special lazy collection in all the entity's navigational properties (such as medical.Clinics), so that it will fetch the related data only when you actually try to access it, i.e. by enumerating the collection in any way.
Comparatively, if you were using eager loading, EF wouldn't do this for you and the nav prop simply wouldn't be filled in with anything unless you explicitly called Include on it.
Updating untracked entities
In your second example, you are working with an entity object which was not created by Entity Framework. You made it yourself:
var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
And now you manually add it to the change tracker:
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
There's nothing wrong with this, but you have to realize that the entity in the change tracker was not generated by EF, and therefore it doesn't contain these special "lazy navigational properties". And because it doesn't contain these lazy navigational properties...
var sizeClinics = medical.Clinics.Count;
... the above code doesn't actually try to fetch the data from the database. It simply works with the entity object you generated and what it already contains in-memory.
And since you didn't add anything to medical.Clinics yourself, the collection is therefore empty.
The answer
Lazy loading only works on entity objects generated by EF, not on entity objects generated by you, regardless of whether you manually added it to EF's change tracker afterwards or not.
So to get the count, you can specifically query the clinics from the database:
var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
var clinicCount = ctx.Clinics.Count(p => p.MedicalId == medical.ID);
Or you could detach the entity and fetch it from the db, though I'm not a fan of this:
var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();
// Detach
ctx.Entry(medical).State = EntityState.Detached;
// Now fetch from db
var medical2 = ctx.Medicals.FirstOrDefault(p => p.ID == medical.ID);
var sizeClinics = medical2.Clinics.Count;
Why detach? Remember how I mentioned that EF only allows tracking of one entity of a given type and PK. Since the object referred to by medical is already being tracked, you can't fetch and track another new instance of Medicals with the same PK.
By detaching the first, medical2 can be fetched and tracked since the change tracker "forgot" the other instance.
But to be honest, it would be easier to just open a new context instead of trying to manually detach and re-query.
var medical = mapper.Map<MedicalViewModel, Medicals>(medicalViewModel);
ctx.Entry(medical).State = medical.ID == 0 ? EntityState.Added : EntityState.Modified;
ctx.SaveChanges();
using(var ctx2 = new MyContext())
{
var medical2 = ctx2.Medicals.FirstOrDefault(p => p.ID == medical.ID);
var sizeClinics = medical2.Clinics.Count;
}
More info if you're interested
If you're using code first, lazy loading is why EF requires you to make these properties virtual. EF needs to be able to inherit from your entity class and make a special derived class which overrides the navigational property behavior.
You already stumbled on this, when you said:
I thing I've noticed: if I analyze medical object with QuickWatch the first time (without SaveChanges part) its as {System.Data.Entity.DynamicProxies.Medicals_650D310387E78A83885649345ED0FB2870EC304BF647B59321DFA0E4FBC78047}.
Instead, if I do SaveChanges and then I retrieve that medical, it is as {MyNamespace.Models.Medicals}.
That System.Data.Entity.DynamicProxies.Medicals_65 (and so on) class was dynamically generated by Entity Framework, inherits the Medicals class, and overrides the virtual navigational properties so that it lazily loads this information when the collection is enumerated.
This is the hidden magic of how EF achieves lazy loading.

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.

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

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();

One-To-Many AND Recursive relation - Force value to be set

Environment
Framework 4.5
Entity Framework 6 (code First)
Model
MainEntity One to many ChildEntity relationship
ChildEntity One to many recursive ChildEntity relationship
Model in code Snippet
public class MainEntity
{
public long Id { get; set; }
public virtual Collection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public long Id { get; set; }
public MainEntity ParentMainEntity { get; set; }
public virtual Collection<ChildEntity> ChildEntities { get; set; }
}
Note : A ChildEntity can only ONE level deep again ChildEntities as childeren.
Problem
I am able to persist this model. This seems to work fine. Only one issue. When I store a ChildEntity that has a parent ChildEntity. The MainEntity_id field is NULL, only the ParentChildEntityId is set. The reason I want to have the MainEntity_Id field always set is for performance queries.
How can I force that the MAINENTITY_ID field has always a value set in the deeper level?
First:
You are giving yourself a hard time by leaving the foreign keys out of your POCO's. Adding a MainEntityId property to your ChildEntity will enable you to set the relation of new ChildEntities to a MainEntity.
Second:
var newChild =new ChildEntity();
parentEntity.ChildEntities.Add(newChild);
parentEntity.ParentMainEntity.ChildEntities.Add(newChild);
should work depending on how you have loaded the entities and which entities are or will be attached to the dbContext.
You just need to define your relationship between MainEntity and ChildEntity to be required. You may do it in two ways:
place [Required] attribute over ParentMainEntity property in ChildEntity
use fluent api. In your DbContext class override OnModelCreating method and in it place code:
modelBuilder.Entity().HasRequired(e => e.ParentMainEntity).WithMany(e => e.ChildEntities);
I would like as well to recommend you to make all your entities properties virtual. When all will be virtual then ef instead of working with your entity classes will create its own DynamicProxy classes deriving from your classes. They will provide additional tracking functionalities, they automatically change values of navigation properties if related objects changes etc. EF seems to deal much better with them. To use that functionality for newly created objects you will need to create them with context.ChildEntities.Create() method instead of using constructor. Of course as this adds constrain on your ChildEntity objects you may encounter exception during persisting data to db in SaveChanges. If the above change is the only one, that you've applied it is very probable that there is at least one ChildEntity object that do not have MainEntity object assigned to it.

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