How does the dbContext track the state of an entity - asp.net-mvc

Im talking about asp.net mvc
so basically an instance of the dbcontext gets initialized, puts the data in the view then it gets disposed of.
how does it track changes made to the entity if the "entry" which contains the original and present value get ...well disposed of.

Well, it doesn't.
Let's say you fetch an entity from the database for an edit view. Then the edit view is generated from the entity. Now the context is disposed, as it is not needed anymore. We have all the data needed to create view. Context doesn't track any changes you do in the view and when you think about it, how could it anyway?
Now you post the edit view. Context has no idea that the model has been changed. On the edit action method you mark the posted entity as dirty with db.Entry(entity).State = EntityState.Modified that doesn't do anything yet really, but when you call db.SaveChanges all the dirty entities are updated, added or deleted. After this the context is disposed again.
The point is EF doesn't track the changes for you, it's you who decides which entities are being updated. It updates the entity yes, but it doesn't know what has been changed since the last update (atleast I think so, why would it need to track the changes?).

Related

What happens to the other NSManagedObjects on a context when context.reset() is called?

I have a scenario where an Entity has many relationships with other entities. I did some changes in the NSManagedObject of the entity and discarded those changes.
Right now I'm calling managedObjectContext.refresh(entity, mergeChanges: false) and then managedObjectContext.refresh(relatedEntity, mergeChanges: false) on every related entity to ensure having no dangling objects in the context.
What would be the difference if I directly call managedObjectContext.reset()? Should I still need to refresh or mark nil the related entities?
Is there any way to make this code more optimized?
If you call reset, you need to also immediately stop using every managed object you have fetched from that context. All fetches need to be redone after the reset, because it makes the context forget about everything it has already fetched.
There are various patterns for more efficiently creating discardable changes like you describe. One popular choice is:
Create a new managed object context and make it a child context of the current context.
Make your changes in that context.
If you want to save changes, save them. If not, just don't bother. The child context will be deallocated and its changes will be lost.

breeze: behavior of setDeleted

I have a grid which shows a list of entities.
Each row has a delete button.
Once the user clicked on the delete button for a given entity I want to change the css of the row and to replace the delete button with a cancel button.
So on the delete button event handler, I do :
myEntity.entityAspect.setDeleted();
But as soon as I do that, the entity is removed from the collection and the row disappear from the grid.
Is there a way to prevent that ? I just want to mark the entity as 'deleted', and postpone any change until user clicks on save button.
The only alternative I see, is to add a property isDeleted to my client-side model and to base my logic on that. But it means I have to handle change tracking myself and loop through the entities on save to call setDeleted for entities which isDeleted property is true. I'm not fond of this solution. Is there something better I'm missing with breeze ?
My guess is that your collection is the value of one of Breeze's navigation properties. These collections are 'live' in that if you delete an entity, the entity becomes removed from all live collections to which it belongs.
So the best answer would be to copy the entities into your own collection and bind to that. Your own collection will not be 'live' and deleting entities will not remove them from the collection.
The problem with 1) is that when you delete an entity we need to sever its relationships with any other entities. This really is the essence of deleting, we want the environment to look like it will after the delete is committed ( via EntityManager.saveChanges())
When you say that you do not want the navigation properties to be set to null after a delete I am assuming that you are talking about a scalar (1->1) or (n->1) navigation. (nonscalar navigations simply remove the entity from the collection) The first issue is whether you are talking about navigations 'To' the deleted entity or 'From' the deleted entity. In the case of 'NOT" removing the navigation 'to' a deleted entity we would be causing many if not most existing breeze apps to fail. Most apps expect to see entities disappear when you delete them.
In the case of 'NOT' removing the navigation 'from' a deleted entity, the concept make more sense except that you cause two more issues. The first is that this breaks any 'bidirectional' navigations, i.e. you can no longer roundtrip between the 'from' and 'to' navigations. But more importantly, we view a 'delete' as a precursor to an eventual 'detach' operation ( which will occur after a successful save). The primary value of the 'detach' is that it recovers memory because we have effectively removed any references to the 'deleted' object, which means that it can be garbage collected. If we leave any navigation properties intact this garbage collection will never occur, and we end up with a memory leak.
We could get around this by having the 'detach' operation also remove navigation properties but the rules begin to get harder to explain.
If you still feel strongly about this, please post your suggestion to the Breeze User Voice. If we see some substantial interest in this, we will try to come up with a solution, but right now we don't have a good answer to this that doesn't add real conceptual complexity. (Something we really try to avoid).
Can you give more detail about the 'validations' that you are seeing? Are these validations on the 'deleted' entity or on the still alive entities that were pointing to the deleted entity? Not having the first occur makes a lot of sense (and we should fix it if this is the case), not having the second occur does not make sense because you really are causing a real validation failure.
Ok, I have tried Jay's solution. First I do a deep copy using lodash.js:
$scope.sourceMaterials = _.clone($scope.request.sourceMaterials, true);
then I bind my grid to $scope.sourceMaterials. User clicks on delete button for a given row and I do:
var document = $scope.sourceMaterials[index];
document.entityAspect.setDeleted();
The problem is that breeze sets to null all the navigation properties of my entity (see removeFromRelationsCore function in breeze.js).
Doing so affects the values shown on screen to the user. For instance one of the required field gets nulled by breeze. So when user clicks on the save button, the validation fails.
To sum up I have two issues:
navigation properties should not be set to null when deleting my entity
the validation should not be triggered for an entity that has its state set to deleted
Could you shed some light on this please ?
EDIT
Following Jay's answer, I can confirm that:
1) navigation property is nulled FROM the deleted entity and I'd like that to be prevented somehow
ex: I have an entity Document with a 1 -> 1 np named DocumentType. If I set deleted on Document, then DocumentType is nulled.
2) validation rules occur on properties of the deleted Entity (those that were nulled)
which one of these problems should I report on user voice ?

What's the correct way to handle new breeze entities that might be thrown away?

I'm sure I've created my own pain here, but I'm struggling to understand the correct sequence of events to manage creating new entities in my scenario.
In my model I have two objects, ObjectA and ObjectB that both inherit from BaseObject, obviously each with their own additional properties.
In my view, as most of the information is the same, I want the user to be able to just select an option as to which one to create. So they fill out SharedProperty1 and SharedProperty2 (which is a collection navigation property), select an option as to if they want an A or B object, and then fill in a final page which has the last object specific properties.
As I didn't know which entity to create until the user had selected this option, I built a an object in the viewmodel to handle this temporary data. As part of that while they are filling out SharedProperty2 (the collection), as they add new ChildObjects, I create them with entityManager.createEntity('ChildObject'). Then when they reach the end, I create either ObjectA or ObjectB entity and add the child entitites (and other properties) and then try and save.
The problem is it never saves correctly, but I get different results depending on which approach I take. So because the user could just abort the new object process, I was creating the ChildObjects with EntityState.Detached because I thought that would be easier if they got thrown away. I realised though that all the entities created in this way get the id key 0. So then I fixed the keys while I was adding the ChildEntites to the parent (either ObjectA or ObjectB), by assigning them decreasing negative numbers (ie: -1, -2, etc). This resulted in some crazy server-side behaviour with only some entities being saved to the db and complaints of conflicting foreign keys.
This also had a bad smell that I hadn't understood this correctly, and I'd made a mess of it. So now I tried just creating the entities normally (ie: without the Detached flag), and they all get their own unique keys (again breeze appears to follow -1, -2, etc), but now when I try to copy them from my temporary viewmodel collection to the parent object collection, I get the error that an entity with this key is already attached. So now I can't even build up the correct model to save.
I still think I've not understood quite correctly how to handle this, so some pointers would be deeply appreciated.
To head off what I suspect will be a question, why I didn't use RejectChanges to handle the entities being thrown away. Basically a user can add a ChildObject (object gets created by breeze entityManager, added to viewmodel collection, bound to UI), and then decide to just remove it again (currently just gets removed from viewmodel collection) before they save their data. If I used reject changes I would throw away other important entites. I think I'm now going to be a good boy and use the proper detach method if someone removes the ChildObject in the view.
If I understand your question correctly, you are trying to create some properties and then add them to a parent objects collection when saving. Correct me if I am wrong, but Breeze not only supports this, but does so very efficiently. Having come from .NET and C# it was very difficult for me to grasp how easy this can be, but this is what I would do if I were you -
var childA = ko.observable();
var childB = ko.observable();
childA(entityManager.createEntity('ChildObject')); // populate your children
childB(entityManager.createEntity('ChildObject')); // populate your children
Then you can edit them in your view, and when you are ready to save simply add them to the collection.
var save = function() {
isSaving(true);
var parent = ko.observable();
return entityManager.getParent(parent, parentId)
.then(setParents)
.fail(catchError);
function setParents() {
childA().parent(parent());
childB().parent(parent());
entityManager.saveChanges()
.then(complete)
.fail(catchError);
function complete() {
isSaving(false);
return Q.resolve(); // Don't know if you have any unresolved conflicts
}
}
};
Basically in this manner we are -
A : Creating the entities
B : Editing them without performing any changes
C : When we call save we are setting their parent navigation property. In my prior ways (be it right or wrong) I would have simply set ParentId(parentId) and let EF figure out how to navigate but (pardon the pun) this is a breeze with Breeze. We could also just as easily pass in a parent observable and not have to go get it from the manager, it just depends on whether we have it already or not.
Another way you could do this if you want to manage the entities separately is to save a single entity at a time with entityManager.saveChanges([childA]) as they are ready. Just pass in an array with a single entity that you want to save. This may be useful if you are working on multiple entities but they aren't all ready for saving and you need to navigate around your app. Unless you call cancelChanges() Breeze will just keep the entity in cache until you are ready to use it again. In this manner, just make a call for entities in the isAdded() state and you can pull 'em back in and edit again.

MVC.net EF Save Automapper View Model not updating linked objects

I'm using MVC.net and I have 2 classes (case and accident) with a many to many relationship, I'm also using auto mapper to copy View Models to EF and vice versa. Now the problem i've come across is when i do this:
Case theCase = Mapper.Map<CaseEditVM, Case>(theCaseEditVM);
theCase.Accidents.Clear();
UOW.Cases.Update(theCase);
The changes to the case are saved but the link table for accidents is not. Ef totally ignores the Accidents changes.
However when i do:
Case theCase = UOW.Cases.GetByID(someid).Include("Accidents");
theCase.accidents.Clear();
UOW.Cases.Update(theCase);
EF correctly saves the accidents property.
So from what i can tell EF ignores the accident property as its not mapped inside EF yet. Make sense however how do i tell it when mapping the View model i want EF to update the linked properties as well?
The simplest way in your case is first attach case to context and clear changes after you attached it. Otherwise you will have a lot of work. There is no magic which would make this for you. EF doesn't know about changes to relations you did on detached entity and what is even worse once you attach the entity to context you already don't know what records were included in the navigation property so you cannot configure context to reflect that (it must be done per every single related entity) without reloading the whole entity and merging changes between detached and attached one.

Attaching object to EF 4 context from MVC view?

I am not sure if that makes any sense, but here is an example.
I have a Category object, that my Service hands to the Controller, which uses AutoMapper to create a CategoryViewModel. Hand that off to the view, serve it to the client.
Now when that gets posted back, AutoMapper creates a Category from the Model sent back, and I hand it to the Service that gives it to the Repository to persist to the database.
My question is, what is the correct way of doing this? I assume the object is a detached object when posted back and I need to attach it to the context, mark it dirty and save changes?
Basically two ways of doing the update of the entity:
Attach the entity to the context, mark it as modified using ObjectStateManager.ChangeObjectState Method, call ObjectContext.SaveChanges Method
Load the original entity from DB, apply changes to the original using ObjectContext.ApplyCurrentValues<TEntity> Method, call ObjectContext.SaveChanges Method
Each of those have their own pros and cons. For example the 1st one does not make round trip to get the original entity but fails to address concurrency as well as tries to update every property of the entity, while the 2nd one works best when employing optimistic concurrency, updates only changed properties, but it does make extra trip to Db to get the original entity.
"I assume the object is a detached object when posted back and I need to attach it to the context, mark it dirty and save changes?"
Yes.
Any one of the links on this page should help:
http://www.google.com/search?rlz=1C1CHFX_enUS410US410&sourceid=chrome&ie=UTF-8&q=working+with+dicsonnected+entities+entity+framework

Resources