There is the following 2 entities, with the following properties:
Parent
ID
Children
Child
ID
ParentID
Parent
Now I have the following code:
db.Configuration.AutoDetectChangesEnabled = false;
var child1=new Child();
parent.Children.Add(child1);
db.ChangeTracker.DetectChanges();
parent.Children.Remove(child1);
var child2=new Child();
child2.Parent=parent;
child2.ParentID=parent.ID;
db.Children.add(child2);
At this point, child1 and child2 are completely identical. The Parent and ParentID properties have the same values (that of parent). Examining the dbContext entry for both of them also shows exactly the same information, e.g. OriginalValues is empty for both.
If I now call db.ChangeTracker.DetectChanges however, child1.Parent becomes null, while child2.Parent keeps its value. How does EF know to do this - where does it keep the info needed to be able to make this difference?
Thank you for any ideas
In general
Detect Changes works by detecting the differences between the current property values of the entity and the original property values that are stored in a snapshot when the entity was queried or attached.
Entity Framework Automatic Detect Changes
and
Ensures that ObjectStateEntry changes are synchronized with changes in all objects that are tracked by the ObjectStateManager.
ObjectContext.DetectChanges Method
so in your example after you disable change tracking the new values are on top of the original values until you decide to update the model by calling context.ChangeTracker.DetectChanges and synchronize all objects (it more or less defers the operations). Now when you call DetectChanges all operations (Remove & Add) get commited to the model so that they can be saved later. Thus the parent is removed from the child1 parent.Children.Remove(child1) and child2 keeps its value because here the operation was to add the parent (assignment).
The reason why you would want to disable change tracking is performace:
If you are tracking a lot of entities in your context and you call one of these methods many times in a loop, then you may get significant performance improvements by turning off detection of changes for the duration of the loop.
If you'd like to know the exact technical/implementation details you can digg through the source code:
.NET Framework source code online > DetectChanges
Related
Background
In my application we were running into issues when trying to add a new entity with existing children to the database after mapping it from a DTO using AutoMapper. The entity and its children were not attached to the context, and so the children would be treated as new and EF would attempt to insert duplicates. My solution to this was to automatically attach an entity to the context whenever an object was mapped to a BaseEntity type (BaseEntity is the base class for all of our Model objects, it has an Id property and nothing else). Here is the code:
public TDestination Map<TDestination>(object source) where TDestination : class
{
var result = _mapper.Map<TDestination>(source);
if (typeof(TDestination).IsSubclassOf(typeof(BaseEntity)) && result != null)
_context.Attach(result); //_context is a DbContext
return result;
}
This worked fine in my initial test cases, but now I've run into an issue where the entity I'm attaching has a child that is already attached to the context. This throws "The instance of entity type 'MyChildEntity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.".
How can I attach an entity to the context when a child is already attached? I'm trying to keep this method extremely generic so that it can be used by any object that we are trying to map from a DTO to a BaseEntity.
What I've Tried
I've tried grabbing the associated EntityEntry and recursively detach all of its children using the following method before attempting to call Attach():
private void DetachChildren(EntityEntry entity)
{
foreach (var member in entity.Members.Where(x => x.CurrentValue != null))
{
if (IsBaseEntityType(member.CurrentValue.GetType()))
{
var childEntity = _context.Entry(member.CurrentValue);
childEntity.State = EntityState.Detached;
DetachChildren(childEntity);
}
}
}
I've confirmed that this loop does reach the problem child and sets its state to detached, but I still end up getting the same error when calling Attach().
Welcome to the hell that is working with detached entities.
Automapper can be leveraged to update existing entities rather than forming an entity and attaching it. Given an object like an Order:
public void UpdateOrder(OrderDTO orderDTO)
{
var order = _context.Orders.Single(x => x.OrderId = orderDTO.OrderId);
_mapper.Map(orderDTO, order);
_context.SaveChanges();
}
The benefits of this approach is that it handles whether the order happens to be tracked or not, asserts the order exists for something like an Update where it is assumed it does, and when SaveChanges runs, only the fields that actually changed will be updated. If only 1 field changed, the update statement updates that single field. Attaching a new object and setting EntityState to Modified will update all fields. This could introduce unexpected attack vectors to change data you don't expect since a DTO needs to pass enough info to construct a whole entity to avoid unintentionally #null-ing data. The mapping from DTO to entity should ensure that only editable fields are copied across.
In the case where the OrderDTO will contain one or more child collections to update, you will likely need to use a mapping that excludes those collections, then use AfterMap in the mapping configuration to inspect the child collection for new vs. existing vs. removed entities and handle those accordingly. (Add vs. mapper.Map vs. Remove)
Generally the updates can be structured to perform atomic operations that make the entity interactions as straight forward as possible. For instance UpdateOrderDetails(orderDTO) would update information about the order itself, where there would be separate methods such as AddOrderLine(newOrderLineDTO) vs. UpdateOrderLine(orderLineDTO) vs. RemoveOrderLine(orderLineId) etc. rather than having all order line operations and other related changes done through a single UpdateOrder method accepting a whole modified object graph.
The alternative when dealing with graphs and the possibility of tracked entities is that you need to check each and every related entity against the DbSet's .Local or other means to check to see if the entity is tracked. If it is, then you have to replace the references and copy any applicable changes to the already tracked entity. Telling a DbContext to ignore an existing entity isn't always a simple matter as there can be references to that entity in other tracked entities. Generally you'll want to detect a tracked entity reference then update your references to use that tracked reference and update it as needed. It is lots of mucking around with references, and definitely does not work well with Generic methods
Generic operations are tempting from a DNRY standpoint, but dealing with situations where some entities might be tracked vs. not, and then handling type mismatches etc. (source = object = can be anything..) adds a lot of complexity in place of simpler methods to handle operations.
Given this model:
I would like to be able to save in one SaveChange call the relations. Which means, I either have a new or updated ContainerParent, and multiple first level children and each of those can have 1 or 2 levels deeper.
The thing is, the children both have a key to themselves, for finding its parent, and a key to the container, for the container to get all its Children independently of their hierarchical level.
With this pseudo code (in the case of all entities are created, not updated)
var newContainerParent = context.ContainerParents.Add(new ContainerParent());
var rootChild = context.Children.Add(new Child());
var secondLevelChild = new Child();
var thirdLevelChild = new Child();
secondLevelChild.Children.Add(thirdLevelChild);
rootChild.Children.Add(secondLevelChild);
newContainerParent.Children.Add(rootChild);
context.SaveChanges();
Problem with this code, is that only the rootchild will have the FK for the container set. I also tried to add the children to they child parent AND the container:
rootChild.Children.Add(secondLevelChild);
newContainerParent.Children.Add(rootChild);
newContainerParent.Children.Add(secondLevelChild);
newContainerParent.Children.Add(thirdLevelChild);
I have the same problem while updating an existing container with new children. I set all the children with the already existing key of the parent, but when SaveChanges is called the key is not saved, its reverted to null.
I fixed it by doing all this in 2 steps, saving once and then getting all the newly created children and updating them with the parent key, the calling SaveChanges again.
I have a feeling I'm missing something, that I should not need to save twice.
The number or frequence of SaveChange calls have no implication on anything, not on performance or so. So why do you want to minimize it ?
Actually, storing such a self referencing table with one SaveChanges is not possible,
cause the ID of an new entity, is generated, when it is saved. So you first need to save it, and then you get the ID, that you can store in another entity. This might require further update-Commands, to the entity you just stored.
You have two chances to solve this.
1) manually generated ID's, handle it all yourself and you know the ID before your store it.
2) In case you have no circularity in your dependency, so a perfect tree structure, you save the items top-down, level by level. I assume you have the childs having a reference to it's parents, so the root has no reference to any other items, you save that first, than the 1st level children, and so on.
This requires multiple SaveChanges, but this is not a disadvantage. It is one Insert-SQL-Command per entity anyway, no matter if you do it in 1 SaveChanges or in 100 SaveChanges.
Both solutions avoid "Update" Commands to the entities, they do Inserts only.
Entity Framework could actually find out this dependencies itself and create an order for new entities to insert, but this is not implemented today, or not perfect, especially on self-referenced tables. The order of saving items is kind of random. So you have to enforce the order with intermediate SaveChanges.
This question isn't code-centric, it's a question about idioms. I'm using Backbone/Marionette on the front and C#, NHibernate in the back.
I've got a few tables mapped and working for creates and updates. For simplicity, say I've got two tables, a parent and child, and the child has potentially many rows. The child reference to the parent is not-nullable, so I've got an Inverse relationship, and that's all working. The flow of data from Backbone to the Controller to NHibernate is pretty simple, and I'm using ISession.SaveOrUpdate() at the end. I can post mappings and so forth if necessary. I will say that the mappings Fluent NHibernate generates uses a Bag on the parent.
Here's a concrete example of the situation I'm trying to understand. Say I've got a parent entry with two rows in the child table. I manipulate the data so that one of the children is removed, but no other changes are made. Javascript sends over an object "tree" with the parent entry and the one child row that's left. The mappings are all handled fine, but the sql that's generated is a bunch of (unnecessary, but whatever) update statements. What I would like to happen instead is that NHibernate notices that there is only one child relationship in this new object, but there are two children in the actual database, and then NHibernate deletes that other child. The 'cascade-delete-orphans' option is not working, because the other child isn't actually being orphaned. It still has a reference to the parent in the fk column, and that column is non-nullable anyway, which is why I used the Inverse mapping option.
Is that possible to setup in the mappings? If not, what is a good way to tackle this situation?
Since you are sending an object from the client side, and then create the entity from that object and try to persist, NHibernate will not automatically delete the child entity since it does not know the child object is deleted (it only see you are only try to update one parent entity and a child entity), which is correct in my opinion. For example if you want to just update the parent entity field, then you have to load entire object graph to do it, otherwise NHibernate will delete all children since they are not loaded.
What you should do here is to load the parent entity, and remove missing child entity(s) deleted from it and then persist (instead of mapping the entity), code should look like following,
void Update(ParentDto parentDto){
Parent parent = _session.Get<Parent>(parentDto.Id);
//update parent fields
var childRemoved = //find removed child from parent;
parent.Children.Remove(childRemoved);
_session.SaveOrUpdate(parent);
}
I am working on a CAD application, where I have block entity. Each block entity have a list of child entities. When these entities are rendered, every block entity knows about its child entity (as it can figure out the child entity in the list), and hence when block entity is selected, the whole block entity along with its child entities gets selected. However, child entity does not know the parent block and other child entities of that block, and due to this when child entity is selected, I cannot get the whole block entity along with all its child entities selected.
As a fix to this problem I created a property in child entities to hold the reference of parent block entity. But, then there might be some issues with cross-referencing and making my data structures error prone.
For Ex: Having a Copy command, somebody working on these data structures few days from now, might just copy the same parent while creating a copy of child entity. However, new copy should belong to some other parent block.
Please suggest the better way to implement this relationship, so that when a child entity is selected I can select whole block entity along with all its child entities.
public class BlockEntity
{
public List<ChildEntity> Children = new List<ChildEntity>();
}
public class ChildEntity
{
public readonly BlockEntity Parent;
}
I have recently come across this issue. I, and others I talked with, came up with two choices:
Do what you are doing with the Parent<-->Child relationship, both knowing about each other.
Have a Parent-->Child relationship and make everything be handled at the parent.
Both are viable solutions, but both have their issues. The first case, what you are doing, seems better and Microsoft seems to use this with their TreeView/TreeNode and DataTable/DataRow/etc. objects for example, because they can each reference back to their respective parents.
Maybe add constraints to the parent, such as not allowing access to the parent's child-collection directly, but only allowing an AddChild function in which you can do the necessary linking. Or do what #Irfan suggests and have the child require you pass the parent to its constructor. Constrain your copy method as well, but always document everything to remove as much confusion as possible.
The later of the above examples is a little easier as everything is always accessed from the parent. This was our solution. We have many functions in the parent to check for and manage the children within. So in this case you would select the child in the CAD app then go to the parents and check in their collection to see if the child exists there. If it does you select the parent and the rest of the children.
It's up to you, but in each case you need to add constraints and error checking to make sure things happen as close to your desired way as possible. Hope this helps.
what reference problems will you get by creating a reference to parent? With this readonly reference which can only be set during construction of the object I see no problem at all.
The Contructor (I am sure you know) looks like:
public ChildEntity(BlockEntity p)
{
Parent = p;
}
//Test just to show Parent can not be assigned elsewhere
public void test()
{
//this line below will show compile error
Parent = new BlockEntity();
}
Do you think there would be some problem with this. The list is a loose reference so there is no stack overflow exception.
I have an intermittent problem in a production system that I haven't been able to recreate locally. What appears to be happening is that for no obvious reason during an edit of a child entity and saving the parent containing the collection the parent association on the child entity is removed in the database. Effectively orphaning the child entity.
Here is the fluent mapping:
mapping.HasMany<ExpenseItem>(x => x.ExpenseItems).Cascade.AllDeleteOrphan();
The entity can also be directly saved (it is also aggregate root), but during testing this has not be shown to remove the parent association.
There are no references to the parent itself in the child object the association is all in the DB handled by Nhibernate.
There is also no possibility of assigning the same child entity to another parent as expense item (child entity) cannot be added outside of its parent directly. See code below.
public virtual ExpenseItem AddNewExpenseItem(ExpenseAnalysis analysis,
string recipientName,
string purchaseAccountReference,
string expenseDescription,
string expenseNotes,
Money value,
VATAnalysis vat)
{
Validate.IsNotNull<ExpenseAnalysis>(analysis);
Validate.IsNotNull(expenseDescription);
....
ExpenseItem newExpenseItem = new ExpenseItem(analysis,
recipientName,
purchaseAccountReference,
expenseDescription,
expenseNotes,
value,
vat,
expenseItemUniqueReference,
true,
Candidate.Assessment);
_expenseItems.Add(newExpenseItem);
....
Any ideas?
It would definitely help to see your mapping but take a look at
I'd also map the ExpenseItem with a reference to its parent
How to delete child object in NHibernate?
basically you ve to set inverse on the relationship so that NHibernate does not try to update the deleted record with a null victim. And you set Cascade.All | Cascade.DeleteOrphans so that we aren't just breaking the relationship by nulling out the victim, but deleting the entire child record.
This problem was caused by a bug in NHibernate. I had set the session to flush never however when you call isdirty on the session the session was being flushed first. The user was deleting the expense item then cancelling the edit but during the isdirty check the deletion was being flushed. Managed to work around this by changing how the session was being managed for this type of dialog.