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).
Related
I just updated an entire WCF app from EF4 / AutoMapper 1.1 to EF6 / AutoMapper 6.0.0.2 and the behavior is not completely the same.
This doesn't work for me :
Entity Framework - Add Child Entity
Before :
child.Parent = parentObject
OR
parentObject.Children.Add(child)
had the same result in real time (while debugging == before SaveChanges), so I decided to use child.Parent = parentObject for the readability. child.Parent = parentObject added a child in parentObject automatically. The child was also added to the db.
Now : child.Parent = parentObject is not enough anymore (child is not added in the db), I have to add parentObject.Children.Add(child). Sometimes I need the link child.Parent = parentObject, so I have to write both lines. Can someone explain to me why it does not work anymore ?
Also :
I could write before :
Mapper.CreateMap< Patient, PATIENTENTITY >()
.ForMember(dest => dest.Gender, opt => opt.ResolveUsing< PatientGenderResolver >())
.ForMember(dest => dest.REF_GENDER, opt => opt.Ignore())
where dest.Gender is the PK(int) and PatientGenderResolver find the id(int) of the Gender in the table REF_GENDER. This mapping was enough to set PATIENTENTITY.REF_GENDER in real time thanks to the Id resolver.
Now the id is set but PATIENTENTITY.REF_GENDER remains null.
Also I tried to set directly PATIENTENTITY.REF_GENDER with a resolver but it add a Gender in the table REF_GENDER...
So again, can someone explain to me why it does not work anymore ?
EDIT
Some precisions :
Before :
patientEntity = Mapper.PatientToEntity(patientModel);
//patientEntity.REF_GENDER is null
Context.PATIENTENTITIES.AddObject(patientEntity);
//patientEntity.REF_GENDER is set !
Context.SaveChanges();
Now :
patientEntity = Mapper.PatientToEntity(patientModel);
//patientEntity.REF_GENDER is null
Context.PATIENTS.Add(patientEntity);
//patientEntity.REF_GENDER is still null !
//patientEntity.REF_GENDER = Context.REF_GENDER.Find(patientEntity.Gender);//I am obliged to add this line everywhere for every REF !
Context.SaveChanges();
My guess is that the two problems I have are related
EDIT
I just go back in my project. I now have EF6 and Automapper 1.1.
The problems are exactly the sames so I guess Automapper is not involved.
EDIT
I get around the REF_GENDER issue with
patientEntity = Mapper.PatientToEntity(patientModel, Context);
public PATIENT PatientToEntity(Patient patient, EntityContainer context)
{
PATIENT entity = AutoMapper.Mapper.Map<Patient, PATIENT>(patient);
if (patient.Id == null || patient.Id == Guid.Empty)
entity.PatientId = Guid.NewGuid();
else
entity.PatientId = patient.Id;
entity.REF_GENDER = context.REF_GENDER.Find(entity.Gender);
return entity;
}
Apparently, the context has to be the same otherwise a new REF_GENDER is added to the db
You don't mention it explicitly, but you didn't only move from EF 4 to 6, but also from ObjectContext to DbContext. That's a tremendous difference in behavior of the entity classes.
In the ObjectContext API the generated entity classes were stuffed with code that cooperated closely with the context they were involved in. A reference property like child.Parent would look like:
public Parent Parent
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Parent>("model.FK_Child_Parent", "Parent").Value;
}
set
{
((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Parent>("model.FK_Child_Parent", "Parent").Value = value;
}
}
So setting this property is setting the Value property of an EntityReference<Parent>. EF4.1's code isn't public, so we can only guess what happens inside. One thing is clear: it changes childs state into Added -- if child wasn't yet attached to the context.
Fortunately EF abandoned this rigid vendor lock-in when it released the DbContext API (in EF 4.1 no less). Now this generated property isn't anything but an auto-property:
public Parent Parent { get; set; }
This made it much easier to unify the database-first and code-first modes of working with EF. In fact, both code-first and database-first entity classes were POCOs now.
The price (if you like) is that EF can't keep as close a track of everything that happens as it could before. Before, all entity classes inherited from EntityObject, and EF could keep track of all of their interactions. The statement...
child.Parent = parentObject;
would draw the yet unknown child into the context through the attached parentObject.
Now, when someone sets child.Parent, no one but child knows what happened, not even Parent. Which means: there is no way whatsoever for EF to become aware of this change when its change tracker executes DetectChanges (as it does very frequently).
That's why using DbContext you have to add a new child to the context yourself, either by explicitly setting its state, or adding it to context.Children. Or by adding it to parent.Children, which is a change the change tracker can detect, if parent is attached.
I have an entity model with a self relation (parent/child). My entity named Article has a property called parent. This parent is in fact the relation, and ParentID which is the field in the relation. In ef 4 i did this:
using (var dbContext= new DataBaseModel())
{
ArticleTable newEntity= new ArticleTable();
newEntity.name="childArt";
newEntity.ParentID = 1;
dbContext.ArticleTable.Add(newEntity);
dbContext.SaveChanges();
//after calling save I can do this
var parentName = newEntity.Parent.Name;
}
With entity framework 6, this doesn't work any more, I have get the entity from the database again in order to get the related parent entity. Is this because of changes to lazyloading? what should i do different.
The difference is that in EF 4 entities were generated with piles of code that took care of change notification and lazy loading. Since then, the DbContext API with POCOs has become the standard.
If you want the same behavior as with the old 'enriched' entities you must make sure that lazy loading can occur by a number of conditions:
The context must allow lazy loading. It does this by default, but you can turn it off.
The navigation properties you want to lazy load must have the virtual modifier, because
EF must create dynamic proxies. These proxies somewhat resemble the old generated entities in that they are able to execute lazy loading, because they override virtual members of the original classes.
The last (and maybe second) point is probably the only thing for you to pay attention to. If you create a new object by new, it's just a POCO object that can't do lazy loading. However, if you'd create a proxy instead, lazy loading would occur. Fortunately, there is an easy way to create a proxy:
ArticleTable newEntity= dbContext.ArticleTables.Create();
DbSet<T>.Create() creates dynamic proxies -
if the underlying context is configured to create proxies and the entity type meets the requirements for creating a proxy.
Database background:
-Parent
|- ChildCollection (with dates)
|- ChildStatus (FK in Child object. ChildStatus is a lookup value)
I use child in the context of the database relation. Inheritance is not a factor here.
What I would like is, in my parent object, to get from the database and store the latest date in the child collection, and the status attached to that child object. The ultimate goal would be to be able to say Parent.LastDate (as a DateTime object) and Parent.LastStatus (as a ChildStatus object)
I've found a number of posts on how to deal with a calculated field from within the parent, or a count for the number of children, but not a way to actually eagerly load a reference to the grandchild entity without explicitly loading the child collection.
I'd like the two objects available to me if I simply pass Parent as my model in MVC without including the child entities.
I guess the better question is "how should I be doing this?" Is this a case where I should create a view and populate the additional properties in the view? I haven't worked with views yet, so this may be a simple misunderstanding of the ideal architecture on my part...
If I understood it right, this is how your code should look like:
var latestChild = Parent.ChildCollection.OrderByDescending(c => c.Date).FirstOrDefault();
if(latestChild != null)
{
Parent.LastDate = latestChild.Date;
Parent.LastStatus = latestChild.ChildStatus;
}
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.
I have a user entity that contains a collection of survey entities. i would like the assocation to include a filter on the relationship, such as 'IsCompleted', so whenever i eager load (or lazy load for that matter) the collection, this filtering happens.
Is this something we have control over?
thanks!
If you are using a DB back-end that supports views, you might consider using the view as the source for the collection of survey entities. Leverage the power of the DB to do that filtering for you.
Loading of associations for an entity always just gets them all, whether because you used Include during the initial query, called Load after the fact, or lazy-loading caused it. The concept of the navigation property kind of assumes this behavior.
E.J. Brennan's answer would work well. If you're not concerned about loading all surveys behind the scenes (because of performance/memory reasons or something) then you might also consider creating a separate property via a partial class definition on your entity that returns the filtered list.
public partial class User
{
public ICollection<Survey> CompletedSurveys
{
get { return Surveys.Where(s => s.IsCompleted); }
}
}