When marking an entity for deletion, and saving changes, sometimes the operation fails due to foreign key constraints for example. In this case, i notify the user and refresh the entity. The problem is that the entity does not get fully refreshed - its foreign keys stays empty.
For example :
//EditableEntity.SomeCollection --> Populated...
ContextManager.CurrentObjectContext.DeleteObject(EditableEntity);
try
{
ContextManager.CurrentObjectContext.SaveChanges();
}
catch (Exception err)
{
ContextManager.CurrentObjectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, EditableEntity);
}
//EditableEntity.SomeCollection --> Empty!!
Please help,
Thanks,
Oran
Well, it seems that after recreating the EDM, and rebuilding the project, the above procedure works fine.
To conclude the solution : When marking an object for deletion, it behaves as it was 'detached' from the object context losing its related parents and collections but is actually still in the 'attached' state. If the delete opertaion is canceled of any reason, the usage of this object related objects and collections, will raise an exception. To solve this we need to 'attach' the object back to the object context. The problem is that the object is still marked as 'attached'.
To solve this simply refresh the object using :
ContextManager.CurrentObjectContext.Refresh(RefreshMode.StoreWins, item);
Hope it helps,
Oran
Related
Lets take an example where there are two type of entites loaded: Product and Category, Product.CategoryId -> Category.Id. We have available CRUD operations on products (not Categories).
If on another screen Categories are updated (or from another user in the network), we would like to be able to reload the Categories, while preserving the context we currently use, since we could be in the middle of editing data, and we do not want changes to be lost (and we cannot depend on saving, since we have incomplete data).
Since there is no easy way to tell EF to get fresh data (added, removed and modified), we thought of twp possible ways:
1) Getting products attached to context, and categories detached from context. This would mean that we loose the ability to access Product.Category.Name, which we do sometimes require, so we would need to manually resolve it (example when printing data).
2) detaching / attaching all Categories from current context.
Context.ChangeTracker.Entries().Where(x => x.Entity.GetType() == typeof(T)).ForEach(x => x.State = EntityState.Detached);
And then reload the categories, which will get fresh data. Do you find any problem with this second approach? We understand that this will require all constraints to be put on foreign keys, and not navigation properties, since when detaching all Categories, Product.Category navigation properties would be reset to null also. Also, there could be a potential performance problem, which we did not test, since there could be couple of thousand products loaded, and all would need to resolve navigation property when reloading.
Which of the two do you prefer, and is there a better way (EF6 + .NET 4.0)?
Sounds like you want to try
/// <summary>
/// Reloads the entity from the database overwriting any property values with values from the database.
/// The entity will be in the Unchanged state after calling this method.
///
/// </summary>
public void Reload(){
....
}
sample call
public virtual void Reload(TPoco poco) {
if (poco == null) {
// wtf....
}
try {
Context.Entry<TPoco>(poco).Reload();
}
catch (Exception ex) {
//... whatever
throw;
}
}
You can use a second level cache implementation, for example:
SECOND LEVEL CACHE FOR EF 6.1
Note: it's no longer beta version. the 1.0.0 what released on 2013/8/27. There are also other implementations, including some for older versions of EF.
I'm closely following John Papa's pluralsight course on Angular and Breeze. I also use Entity Framework 6.
At load time, I call a Prime function that clears the cache:
function clearCache() {
var cachedParents = manager.getEntities('Parent'); // all invoices in cache
cachedParents.forEach(function (parent) { manager.detachEntity(parent); });
zStorage.clear();
manager.clear();
}
and then, loads the info:
return EntityQuery.from('Parents')
.where('applicationUser.email', '==', userId)
.expand('Address, Children')
.toType(entityParent)
.using(self.manager)
.execute()
.then(querySucceeded, self._queryFailed);
that calls the controller with
[HttpGet]
public IQueryable<Parent> Parents()
{
return _repository.Parents;
}
That returns one record...
Later, on the loading of the view, in the same repository, I request the parent entity from the local cache as follows:
var cachedParents = EntityQuery.from('Parent')
.toType(entityParent)
.using(self.manager)
.executeLocally();
THIS ONE BRINGS TWO ENTITIES, the correct one with Id, name, addres, etc. but also brings an empty entity with Id 0.
I've checked and even if I call the local query right after the remote query, it still brings the correct record AND the empty record.
I also reviewed the response and the json object comes correctly formatted and with only one record.
I've tried clearing the zstorage, the entity manager, detachment of the object, but nothing seems to explain or clear the empty entity.
This behavior only happens in the Parent entity type. No other type shows anything wrong.
Thanks in advance.
One way to debug this is to subscribe to the entityManager.hasChangesChanged event.
This event will be fired when the "ghost" entity is added. This way, you can trace the call stack by putting a breakpoint inside the event.
So first, ensure that after the clearCache call, the entityManager is empty. (Side note: the call to individually detach the parent entity via manager.detachEntity is actually redundant since you're already calling the manager.clear method at the end)
Then, put a breakpoint inside the hasChangesChanged event as you debug.
Hope this helps.
My bet is that you have code that adds a "nullo" (a placeholder entity with id=0) to the EntityManager. There is such code in John's sample and you might be calling it unintentionally.
To demonstrate that Breeze is NOT adding such a nullo itself,
set a breakpoint before the query
set a breakpoint at the top of the querySucceeded
confirm that there are no entities in cache at all before you query for "Parents" (e.g., manager.getEntities() returns nothing).
confirm at the top of querySucceeded that the query results in exactly ONE entity in cache (e.g., manager.getEntities() returns the lone "Parent" entity).
FWIW, there is no need to detach individual entities of type "Parent" if you are going to call manager.clear(). That call detaches every entity in the manager's cache, including the "Parent" types.
How are folks generally handling the whitelisting of foreign key values? Let's ignore the use case of an associated user record which brings an additional set of issues and stick to a fairly benign scenario: A Task belongs to a Project. When I create the task, I want to create it with its project_id value, but I don't want that value to be editable. The property is passed by a hidden field in the shared form.
I know I could just unset that property in the controller before calling save() in the edit action, but I was wondering whether anyone had a better solution. I've used/tried several, but all are laborious or less "universal" than I'd like.
Does anyone have a solution that they really like to solve this particular problem?
Thanks.
I handle this manually as well. The process is something like this.
Load object and show the edit screen to the user.
When user submits, take the primary ID and load the object again. Check ownership.
Have a whitelist of user editable fields, loop through those keys and populate your new object, leave everything else alone.
Save.
You could move this into some kind of before save hook or behavior I would say. But this seems the best practice with the RoR feature (we all know what happened in GitHub)
I'm trying to peform an update statement using WCF RIA Services, but everytime I update I keep getting "An entity with the same identity already exists in this EntitySet. Any insight on where I can start looking or figuring out what is wrong?
Step 1
LoadOperation<Analysis> AnalysisLP = ((App)Application.Current)._context.
Load(((App)Application.Current)._context.GetAnalysisQuery().
Where(o => o.ProjectID == Convert.ToInt32(((App)Application.Current).Project.ProjectID)));
Step 2
AnalysisLP.Completed += delegate
{
if (!AnalysisLP.HasError)
{
Analysis = AnalysisLP.Entities.FirstOrDefault();
};
Step 3
((App)Application.Current)._context.Analysis.Attach(Analysis);
((App)Application.Current)._context.SubmitChanges(OnSubmitCompleted, null);
Can anyone help me, what is it i'm doing wrong??
thanks
Your object Analysis comes from the EntitySet via a query but is still attached to that EntitySet.
You just need to change its properties and call SubmitChanges. Do not try to attach it again.
To avoid the “An Entity with the same identity already exists in the EntitySet” exception, Entities that are updated, modified or deleted must always be fully refreshed from server upon saving, there can be no references held in memory to the previous instances of the entities. To prevent orhpaned instances from hanging around, I follow these rules:
Entity instances should not have any property changed event handlers assigned directly to them, rather use OnCreated or OnPropertyNameChanged partial methods instead.
When entities are added to an EntitySet, do not assign parent Entity instance references, use the foreign key ID property instead (myEntity.ParentalID = SelectedParent.ParentalID rather than myEntity.Parent = SelectedParent) because the SelectedParent probably isn’t getting reloaded upon saving because it isn’t part of the unit of work, so that reference will be held after the save and refresh.
Any combo boxes that are used as populate sources for Entity properties of the Entity need to have their EntitySet reloaded after saving as well; otherwise those related Entities populating the combo will hold references to the previous entity instance.
I am missing something very fundamental when working with SL4 RIA entities.
I have a Domain Service with User entities. On the service context, I have a method:
EntityQuery<User> GetUsersQuery()
I perform a load like so:
context.Load(context.GetUsersQuery(), (loadOp)=>
{
// Things done when the load is completed
}, null);
When the Completed action executes, the loadOp.Entities collection is full of the User entities, but they are not attached to the context.Users entity set. It also appears that I can't attach them manually from the callback. What important step am I missing to get these tracked in the entity set?
Just to elaborate, in the completed handler, I tried:
foreach (var user in loadOp.Entities)
context.Users.Attach(user);
And I get an exception that says an entity with that name is already attached.
Yet, both context.Users and context.EntityContainer are empty.
Are you sure you are using the same instance of the context in all cases? What does context.EntityContainer.GetEntitySet<User>().Count say?
Does LoadOperation<User>.HasError return true? If so, what is the error?