How much do you have to "touch" a navigation property to assure lazy loading of a collection?
I am using Entity Framework 5.0 with lazy loading turned on. Consider a simple class:
public class MyResource
{
string name {get;set;}
public virtual ICollection<ResourceEvent> ResourceEvents{ get; set; }
}
When I set up a "foreach" on the collection, I want to avoid individual retrieval of each object in the collection.
using(context = new MyDBContext)
{
MyResource aresource = context.MyResources.Where(a=>a.Name==myname).Single();
//now I want to lazy load the ResourceEvents collection
if(aresource.MyResources!=null) // will this load collection?
{
List<ResourceEvent> alist = aresource.MyResources.ToList();//or must I add this?
foreach(ResourceEvent re in alist)// (or in aresource.MyResources)
{
//do something
}
}
}
I know I can use Include(), but assume the MyResource object comes from somewhere else where we don't know whether collection has been retrieved or not.
You can load collections this way:
context.Entry(aresource).Collection(p => p.MyResources).Load();
For single references use Reference() instead of Collection().
Accessing the navigation property will enumerate the collection which means EF will load all the entities at that time - not one by one. This is important to know because if, let's say, you want the first entity, and you write areasource.MyResources.First(), EF will load all entity objects for that collection even though you're only planning on using one. aresource.MyResources will enumerate the collection AND THEN the First() operation will be performed.
To avoid that, you want to get the IQueryable for that navigation property and build on that. For the example I mentioned, you would do the following:
context.Entry(aresource).Collection( c => p.MyResources ).Query().First()
This statement will only retrieve the one entity from the DB and not all the entities in the navigation property collection.
Related
While using Entity Framework Core with SQL Server I encountered an unexpected problem. I have an entity A that has a one-to-many relationship on entity B.
[Table("client")]
public class Client
{
public long ID { get; set; }
public string Name { get; set; }
public ICollection<Configuration> Configurations { get; set; } = new LinkedList<Configuration>();
}
I get a list of instances of entity A from the database like this:
public ICollection<Client> GetAllClients()
{
return _dbContext.Clients.ToList();
}
When I call this function I get a list of instances without the instances of entity B in the relationship. Why are the objects in the relationship not retrieved correctly?
I've also found out that if I add this line of code to the function the entities are retrieved as expected.
public ICollection<Client> GetAllClients()
{
var test = _dbContext.Configurations.ToList();
return _dbContext.Clients.ToList();
}
This makes no sense to me. What am I missing here?
You can use the Include method to specify related data to be included in query results (Eager loading).
Take a look in the following example:
public ICollection<Client> GetAllClients()
{
return _dbContext.Clients.Include(x => x.Configurations).ToList();
}
You can check the MSDN reference.
Related reference/collection properties must either be eagerly or explictly loaded. You generally want to eagerly load using Include:
var clients = await _context.Clients.Include(x => x.Configurations).ToListAsync();
Alternatively, you can lazy load, but that's generally a bad idea, as it can lead to N+1 query problems (i.e. you issue one query, and then a separate additional query for each item as you iterate through, which is obviously highly inefficient). Regardless, lazy loading requires two things:
The reference/collection property must have the virtual keyword. EF adds lazy loading functionality by creating a dynamic proxy of your entity class and overriding the property getter. Overriding, of course, can only be done on virtuals.
You have to explicitly add the lazy-loading services:
services.AddDbContext<MyContext>(o => o.UseLazyLoadingProxies()
.UseSqlServer(connectionString));
It works when you query the configurations as well, because EF has object-fixup. In other words, it will automatically fill related reference/collection properties, if it has previously retrieved those objects already and has them in its object cache. Otherwise, and if you do not otherwise load the relations, the property will remain null.
Assuming that I have an entity with virtual nagivation property like this:
public class School
{
public long Id { get; set; }
public virtual ICollection<Students> Students { get; set; }
}
As I understand, EF6 uses proxy to enable lazy-loading of Students, but do the following LINQ queries:
var myStudent = this.Students.Single(x => x.Id == id);
var studentsCount = this.Students.Count();
var bestStudents = this.Students
.OrderByDescending(x => x.GPA)
.Take(5)
.ToArray();
result into a SQL query (just like IQueryable<T> does)? Or is it just a lazy-loading collection and will load all students into memory after the first request, and then perform simple IEnumerable<T> LINQ behaviour?
When you query for an entity in Entity Framework, the objects that get returned are not (always) the type of object you think they are. Behind the scenes, it creates a brand new class that inherits from the your class. Because OOP allows a subclass to be stored in a variable typed as the superclass, you never really notice. This is the "proxy" that you mention. That's why the virtual function allows lazy loading. The subclass overrides your virtual method, and contains the code to lazy load the extra data before returning it.
That overridden property call will then check the context to see if the navigation properties are already loaded. If they are, it just returns them. If they are not, it will make additional SQL calls to load them, storing them in the DbContext for later.
In your updated question, my understanding is that running those lines of code would result in 3 separate queries being executed.
Public virtual properties are lazy loading in EF6.
You can disable lazy loading for a DbContext, or use the .Include() method on the IQueryable to include the property in the first query.
http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx
https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
Once you "iterate" through the list (e.g. by calling the .Single(), .Count() or .ToArray() method), the query is executed and you have a in-memory list of your students. See https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution for more details about query execution.
The first example would result in 3 queries, where the first returns the student with the given Id, the second returns the student count and the third returns the first 5 students ordered desc by their GPA property.
Regarding DDD => You can use some Layered architecture, with ApplicationServices and DomainServices, where DomainServices perform the domain logic and ApplicationServices load / transform the data.
The https://aspnetboilerplate.com/ template is for example a good starting point for domain driven design.
Assuming that the second code block is executed somewhere INSIDE the scope of an instance of 'School' (so 'this' is an instance of 'School' - in my example below school19) then the following scenarios are possible:
A) You have loaded your instance of 'School' like this (Lazy loading enabled):
var school19 = dbContext.Set<School>()
.FirstOrDefault(school => school.Id == 19)
Then your 3 lines of code for accessing the property 'Students' will trigger a single additional database hit when
var myStudent = this.Students.Single(x => x.Id == id);
is executed, but no more database hits will occur with the subsequent two statements.
B) In case you have loaded your instance of 'School' like this (Lazy loading enabled):
var school19 = dbContext.Set<School>()
.Include(school => school.Students)
.FirstOrDefault(school => school.Id == 19)
Then your 3 lines of code for accessing the property 'Students' will not trigger an additional database hit.
C) If lazy loading was disabled, then
A) would result in a Null Reference Exception
B) would behave the same
As a last remark, if 'this' was a reference to an instance of the DBContext class, which has a property
public Set<School> Schools { get; set; }
then it would trigger 3 different database calls. But the result is different as this would be executed in the context of ALL schools, while my assumptions above only apply to a single school.
I don't know this is a good or bad thing and how to adapt this.
Parent-Child has very simple and obvious relation.No big deal. You can see the code below:
Parent Object:
public class Parent
{
public int Id {get;set;}
public List<Child> Childs {get;set;}
}
Child Object
public class Child
{
public int Id {get;set;}
public int ParentId {get;set;}
public Parent Parent {get;set;}
}
Somewhere around the code(say index method of the controller)
var parent = parentRepository.FindAll(x=> x.Id == 10).ToList();
var childCollection = childRepository.FindAll(x=> x.ParentId == parent.Id).ToList();
When i run the code above i expect a parent object with Childs Property is null and childCollection that has null Parent property. But this is not happening. After second line of code executed, parent.Childs fills with the child objects and every Parent property in a child object is equal to parent which i don't want it(should i want it?).
Why entityframework behaves like that? And which cases should i be aware off? What happens if i change the childCollection without knowing this behavior?
Yes, that's the normal behavior of EF. At the moment when the childCollection is materialized calling ToList, when you check parent instance again, relationship fixup will run and check if Child entities whose FK have the same value as the PK of your current parent have been loaded earlier in the object context. If that is the case, the Parent.Childs property will immediately be set with those Child entities. This doesn't have nothing to do with lazy loading, in fact your model doesn't meet all the requirements that lazy loading need, like your navigation properties should be virtual.
This behavior cannot be disabled but if you use AsNoTracking extension method at the time to build your queries, the entities returned will not be cached in the DbContext:
var query= context.Childs.AsNoTracking().Where(c=>c.ParentId==10);
You can also find more details in this excellent post
Like many modern ORMs, Entity Framework is designed not only to map your queries results to entities (objects), but to map their relationships to properties. If you declare a navigation property (such as you have done with Childs or Parent properties in your classes), EF will (lazily) map those properties to their referenced entities automatically (you can find more about navigation properties here).
This behavior is by design and has the intent to avoid explicitly do queries using joins, and makes it unnecessary to call childRepository.FindAll(x=> ... because all related Child entities are loaded by EF as soon as you access the Childs property in your Parent object.
Remember also that EF implements a change tracking system, so if you modify entities fetched from the database, EF will track those changes and persists them the first time you call SaveChanges on your context.
As a side note, EF architecture is really an example of the UnitOfWork and Repository patterns used together (DbContext is a UoW and each DbSet works like a Repository), so I strongly suggest you to read more about EF to avoid misusing its features or re-implementing them yourself using another abstraction (e.g. your Repositories).
I have the following class generated by entity framework:
public partial class Branch
{
public short Id { get; set; }
public short CompanyId { get; set; }
public string Code { get; set; }
public string Title { get; set; }
public virtual Company Ts_Companies { get; set; }
}
I have the following method which takes all of the branches out of the database:
public Branch[] LoadBranches(int companyId, int page, int limit, string search, string sort, string sortOrder)
{
using (var dbContext = new TimeShedulerEntities())
{
var _branches = (from ct in dbContext.Branches
where ct.Title.Contains(search) || ct.Code.Contains(search)
select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit);
return _branches.ToArray();
}
}
In my model designer I see that the Lazy Loading is set to true, but when I iterate over the branches, the property Ts_Companies is null. Also I get the following exception:
An exception of type 'System.ObjectDisposedException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: The ObjectContext instance has been disposed
and can no longer be used for operations that require a connection.
Am I forgetting something?
You created and disposed of the context during your function since it was inside the using statement. Each entity happens to know from which context it was created so that lazy loading is possible.
When you accessed the Ts_Companies property, the entity realized that it had not yet loaded that property since it is probably a navigation property and attempted to ask its ObjectContext (TimeShedulerEntities) to load that property. However, the context had been disposed and so that it what caused that exception.
You need to modify your query as follows to 'pre-load' the Ts_Companies:
var _branches = (from ct in dbContext.Branches.Include("Ts_Companies")
where ct.Title.Contains(search) || ct.Code.Contains(search)
select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit);
It will take possibly quite a bit longer to load depending on the size of the Ts_Companies object and how many you end up bringing back at once, but the entity will stop asking its object context to load the Ts_Companies since you would have already loaded them.
A side note: I have found that creation and disposal of object context on a per-method basis causes problems when the entities are passed outside the function. If you want to create and destroy the object context in every function, you probably want to have the function return something that is not an entity. In other words, have an object that can be constructed from an entity and has the properties you need, but don't have it reference the entity. In java these are often called Data Transfer Objects (DTOs). You lose the read-write ability of entity framework, but you don't have unexpected ObjectDisposedExceptions flying all over the place.
The problem comes when you ask an entity to be associated with another (for example, adding on entity to a ICollection property of another entity) when they come from different objectcontexts. This will cause headaches for you since you would have to manually attach the objects to the same context before performing that operation. Additionally, you lose the ability to save changes to those entities without manually attaching them to a different context.
My opinion on how I would do it:
I've found it easier to either have an object containing all of these database access functions control the lifetime of the context (i.e. have your containing object be IDisposable and during disposal, destroy the context) or simply not return entities and have the datastore be read-old, write-new essentially without any modification ability.
For example, I have my object (I will call it my data access object) with a bunch of methods for getting database objects. These methods return entities. The data access object also has a SaveChanges method which simply calls the context's SaveChanges method. The data access object contains the context in a protected property and keeps it around until the data access object itself is disposed. Nobody but the data access object is allowed to touch its context. At that point, the context is disposed by manually calling 'Dispose'. The data access object could then used inside a using statement if that is your use case.
In any case, it is probably best to avoid passing entities attached to a context outside the scope in which their context exists since entity framework keeps references to that context all over the place in the individual entities
But you didn't load your Ts_Companies, use Eager Loading instead:
var _branches = dbContext.Branches
.Where(b => b.Title.Contains(search) || b.Code.Contains(search))
.Include("Ts_Companies")
.OrderBy(c => c.Title)
.Skip((page - 1) * limit)
.Take(limit);
And I came across the same issue before System.ObjectDisposedException, in my MVC project and I didn't use using blocks,instead I define my context on class level.If I need to return and use an array (in my View) I use that context.If I need to just update some information then I have used using blocks.I hope this helps.
I'm pretty new in the ASP .NET MVC world. Maybe, that's the reason I can't explain to myself the cause of what is, for me, an annoying problem.
I have one class with One-To-Many relashionship.
class MyClass{
public List<OtherClass> otherClasses {get;set;}
}
When I'm persisting one instance of this class, I fill it's relationship with an empty List<>
MyClass myClass = new MyClass(){ otherClasses = new List<OtherClass>() }
context.myClass.Add(myClass);
The problem is that, when I try to retrieve that instance, and for any reason, I try to access that list, system gives me a Null Reference Exception...
My question is: why doesn't EF return empty lists instead of null ones? Especially in this case, that I'm persisting it with an empty list?
There's any way to avoid verifing if instances are null?
You should have your entity create those lists in the constructor. EF doesn't create dependent collections, and expects the entity to do so.
So, your case, you would make your entity like this:
class MyClass{
public List<OtherClass> _otherClasses {get;set;}
public MyClass() {
_otherClasses = new List<OtherClass>();
}
}
Make the otherClasses collection virtual. This will enable EF to lazy load the collection.
class MyClass{
public virtual List<OtherClass> otherClasses {get;set;}
}
Otherwise use eager loading with Include method.
context.myClass.Include(m => m.otherClasses).SingleOrDefault(m => m.Id == foo);
So, if I understand correctly, you are adding an empty List<OtherClass> to the context and then trying to retrieve it.
I guess you have to think about how the context will track and query entities that are in its context. This is usually done by the Key of the entity. In your example, you have not given the entity a Key, therefore, the context has no handle on the entity.
Therefore, when you query, the context doesn't find an object and returns null.
If you want to initialize a new entity, I would recommend to give it at least a Key (usually the Id property), and then select by that key when you lookup later.
Hope this helps.