Deleting and then adding a new identical entity throws an error - breeze

I have a model called a DeviceAccount. It is a join table that allows me to create many to many relationships.
I have a function that creates a new DeviceAccount by handing it an account & a device to join. See here:
var createDeviceAccount = function (account, device) {
var initialValues = {
account: account,
device: device
};
return manager.createEntity(entityNames.deviceAccount, initialValues);
};
I have a function to delete a DeviceAccount. See here:
var deleteDeviceAccount = function (account, device) {
var baseQuery = entityQuery.from('DeviceAccounts');
var p1 = new breeze.Predicate('device', 'eq', device);
var p2 = new breeze.Predicate("account", "eq", account);
var modQuery = baseQuery.where(p1.and(p2));
var results = manager.executeQueryLocally(modQuery);
results[0].entityAspect.setDeleted();
};
If I locally create, remove, create, remove the same device/account pair there is no problem.
If I take a device/account pair that exists on the server I can remove it fine, but when I add it again I recieve the following error:
Uncaught Error: This key is already attached:
DeviceAccount:#Test.Models-5:::5
If I follow this in more depth I can see that removing a local device changes the entityState to be 'Detached' and if I remove a device that also exists on the server its entityState gets changed to be 'Deleted'. I can't follow much further than this and I was hoping someone could explain why this could be happening?

Just to be clear, deleting an entity via entityAspect.setDeleted causes its entityState to be set to "Deleted". This action marks the entity for deletion on the next save and also removes it from any navigation collections on the client. The entity is still being tracked by the EntityManager after this operation.
In contrast, detaching an entity via entityAspect.setDetached removes it from the entityManager cache completely. This also removes the entity from any navigation collections on the client, but will have NO effect on the server during an EntityManager.saveChanges call, because the EntityManager no longer "knows" about the entity. Think of "detaching" as telling the EntityManager to completely forget about an entity, as if it had never been queried in the first place.
"Deleting" an entity followed by "re-adding" the same entity is problematic because this would cause the EntityManager to have two incarnations of the same entity; a deleted version and an added version. Therefore the EntityManager throws the exception that you are seeing.
I think what you want to do is delete and add a "new" clone entity with a different id.
Hope this makes sense!

The reason this happens is that Breeze is keeping track of that entity until you have fully removed it from the server to keep you from creating a new entity with the same ID, which of course will throw a server exception since you can't do that.
If you called saveChanges() on your entityManager before you tried to recreate it, then Breeze will go out to the server, remove the entity from the DB, return the promise, and completely detach the entity from the local cache since it no longer exists on the server.
You could set the entityState to detached manually, but then if you try to saveChanges and that ID already exists on the server it will throw an error.
Best Option
Pass the entity into the saveChanges method in an array -
results[0].entityAspect.setDeleted();
manager.saveChanges([results[0]]).then(saveSucceeded);
function saveSucceeded() {
console.log('Entity removed from server');
}
Now after saveSucceeded has completed you can create a new entity with that ID

Related

What is the right way to append entity from one manager to another in breeze?

What is the right way to append entity from one manager to another?
Straight forward attempts result in overwriting entities or in warning about the same entity key...
var entity = em1.getEntities()[0];
em1.deattachEntity(entity);
// assume em2 already has entities
em2.attachEntity(entity);
I believe there should be some in-build functionality for appending entries to another non empty manager or for generating an unique key for selected manager.
Any suggestions?
UPDATE:
I did read the documentation and tried to do it via exportEntities and exportEntities:
var entity = em1.getEntities()[0];
var export = em1.exportEntities([entity], false);
//here em2 already has entity with the same key as entity that I want to add
var import = em2.importEntities(export, { mergeStrategy: breeze.MergeStrategy.Disallowed });
This gives me an error: A MergeStrategy of 'Disallowed' prevents Picture:#Macaw.Whitelabel.WebAPI.Models--1 from being merged
I really don't understand how to append entities....
UPDATE2:
I did discover that manually assigning id of the attached entity solves the problem and error disappears.
Is there a way to make it not manually but using breeze?

which is the difference betwwen this two ways to refresh the dbContext?

I am using EF 4.4 and I would like to update many entities, but some other user can modified many of the entities that the first user is modified. So I get a concurrency exception. Other case is that the first user tries to add many new registers and other user added some of them meanwhile. So I have an exception that exists some of the registers (unique constraint).
I would like to ensure that the first user finish his operation add only the registers that does no exists yet (add all his entities except the entities that are added by the second user).
To do that, I need to update the entities in my dbContext so I see that there at least two options.
First, in the catch when I capture the update exception, I can do:
ex.Entries.Single().Reload();
The second option is:
myContext.Entry<MyTable>(instance).Reload();
I guess that the second option only refreshes the entity that I use as parameter, but if the problem is that I need to refresh many entities, how can I do that?
What really does the first option, Single().Reload?
When you do
ex.Entries.Single().Reload();
you are sure that the offending entity is refreshed. What is does is taking the one and only (Single) entity from the DbUpdateConcurrencyException.Entries that could not be saved to the database (in case of a concurrency exception this is always exactly one).
When you do
myContext.Entry(instance).Reload();
You are not sure that you refresh the right entity unless you know that only one entity had changes before SaveChanges was called. If you save an entity with child entities any one of them can cause a concurrency problem.
In EF 6.x (6.1.3), below code will let you find all the changes; the way you asked in your question!
try
{
var listOfRefreshedObj = db.ChangeTracker.Entries().Select(x => x.Entity).ToList();
var objContext = ((IObjectContextAdapter)your_db_context).ObjectContext;
objContext.Refresh(System.Data.Entity.Core.Objects.RefreshMode.ClientWins, listOfRefreshedObj);
await db.Entry(<yourentity>).ReloadAsync();
return Content(HttpStatusCode.<code>, "<outputmessage>"); ;
}
catch (Exception e)
{
return Content(HttpStatusCode.<code>, "<exception>");
}
Explaination:
Query Entries in the ChangeTracker and store them in a list
var listOfRefreshedObj = db.ChangeTracker.Entries().Select(x => x.Entity).ToList();
Next is to refresh the context. In some cases (row is removed etc.), this will throw an exception which you can catch. RefreshMode.ClientWins tells EF to accept all client units as modified when next update occurs. In some cases, you might want to prompt the users with the changes and let them decide. RefreshMode Enumeration. An example is here ObjectContext.Refresh Method Example
objContext.Refresh(System.Data.Entity.Core.Objects.RefreshMode.ClientWins, listOfRefreshedObj);
You're probably doing this whole thing after you receive DbUpdateConcurrencyException anyways!

Changes not reflected in Database while using entity framework

I am accessing my database through ADO.NET Entity framework in MVC 3 Application.
I am updating my database through Stored Procedure.
But the changes are not reflected at run time.I mean to say i am able to see the changes only after restarting it.
What is the reason for the problem and How can i avoid it ?
I am using Repository pattern So at repository My code look like this
Ther Is One Function Which Save Changes
public void SaveNewAnswer(AnswerViewModel answer,string user)
{
SurveyAdminDBEntities _entities = new SurveyAdminDBEntities();
_entities.usp_SaveNewAnswer(answer.QuestionId, answer.AnswerName, answer.AnswerText, answer.AnswerOrder, answer.Status, user);
_entities.SaveChanges();
}
Data Retreival Code
public IEnumerableGetMultipleChoiceQuestions(string questionId)
{
SurveyAdminDBEntities _entities = new SurveyAdminDBEntities();
_entities.AcceptAllChanges();
_entities.SaveChanges();
return _entities.usp_GetMultipleChoiceQuestions(Int32.Parse(questionId));
}
But Changes are not reflected till the time i don't clode the session of the browser and run it again .
Please help !
Thank You In advance
Are you calling context.SaveChanges() on your Entities (DbContext/ObjectContext) object? Are you using a transaction that you haven't committed?
If you have an uncommitted transaction in your sproc, you can try creating your own entity transaction and seeing if committing your transaction will commit the nested transaction as well. The problem is that calling SaveChanges() automatically begins and commits a transaction, so this may not be any different than that.
I would also call _entities.AcceptAllChanges() in your save operation.
public void SaveNewAnswer(AnswerViewModel answer,string user)
{
SurveyAdminDBEntities _entities = new SurveyAdminDBEntities();
_entities.Connection.Open();
System.Data.Common.DbTransaction tran = _entities.Connection.BeginTransaction();
try
{
_entities.usp_SaveNewAnswer(answer.QuestionId, answer.AnswerName, answer.AnswerText, answer.AnswerOrder, answer.Status, user);
_entities.SaveChanges(); // automatically uses the open transaction instead of a new one
tran.Commit();
}
catch
{
tran.Rollback();
}
finally
{
if (_entities.Connection.State == System.Data.ConnectionState.Open)
_entities.Connection.Close();
_entities.AcceptAllChanges();
}
}
Is your stored procedure doing an explicit commit? Things run in a database session will be available for that session, but not available to any other session until the action is committed.
When you pull data out of your database into your context that data is kept in memory, separate from the actual database itself.
You will see the changes if you create a new context object instance and load the data from the database with it.
It's good practice to not use the same instance of your context object but create them on an as needed basis for individual transactions with the database. In your case if you're updating via function imports instead of the context.SaveChanges() method then you need to refresh your context with the updated data after you commit those changes.
Add this to your connect string (assuming sql 2005)
transaction binding=Explicit Unbind;
if the data is no longer available after session reset, then the problem is indeed with a transaction, if the data is then available after reset, then your problem is something different and we'll likely need more details.

Cannot insert new Employee entity using InsertOnSubmit()

I'm facing this exception An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported. when I try to insert a new entity into my Employees table (the master one).
There is a relationship between the master Employees table and the details Orders table, and I'm sure that the relationship between these two tables (and specifically Employee.Orders EntitySet) is the cause of the problem since when I removed the relationship, it returns back to insert into Employees table with no problems.
When I searched for the problem, there was this blog post which I tried to implement but my case is a different than the one in the blog post in these items:
He faces the exception when tries to update (while I try to insert).
The tables architecture is different.
how can I solve this problem?
Here's the insertion code:
Employee emp = new Employee();
emp.Name = empName; // empName is a local variable
// What should I default emp.Orders to?
dc.Employees.InsertOnSubmit(emp);
dc.SubmitChanges();
P.S: My DataContext is defined on class-level in my repository and the exception is being thrown when I call dc.SubmitChanges();. and I didn't Attach any object why does it say that?
Here is an article explaining what you need to do using the Attach and Detach methods:
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx
I am guessing it is trying to save something else besides just the employee object or you aren't showing us the full code in your repository. When you instantiate your DataContext object (dc) try setting DeferredLoadingEnabled = false, and ObjectTrackingEnabled = false and see if it works. If it does, try watching the SQL code in SQL Server Profiler and see if it is modifying other objects that may have came from a different context like the message says.
var dc = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
My bet is on the primary key.
Are you sure the primary key is also set on auto increment?
Did you
try changing the name, does it work then?
What happens if you remove
all rows from your DB? can you insert one then?

System.InvalidOperationException when trying to iteratively add objects using EF 4

This question is very similiar to this one. However, the resolution to that question:
Does not seem to apply, or
Are somewhat suspect, and don't seem like a good approach to resolving the problem.
Basically, I'm iterating over a generic list of objects, and inserting them. Using MVC 2, EF 4 with the default code generation.
foreach(Requirement r in requirements)
{
var car = new CustomerAgreementRequirement();
car.CustomerAgreementId = viewModel.Agreement.CustomerAgreementId;
car.RequirementId = r.RequirementId;
_carRepo.Add(car); //Save new record
}
And the Repository.Add() method:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
private TxRPEntities txDB;
private ObjectSet<TEntity> _objectSet;
public void Add(TEntity entity)
{
SetUpdateParams(entity);
_objectSet.AddObject(entity);
txDB.SaveChanges();
}
I should note that I've been successfully using the Add() method throughout my code for single inserts; this is the first time I've tried to use it to iteratively insert a group of objects.
The error:
System.InvalidOperationException: The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
As stated in the prior question, the EntityKey is set to True, StoreGeneratedPattern = Identity. The actual table that is being inserted into is a relationship table, in that it is comprised of an identity field and two foreign key fields. The error always occurs on the second insert, regardless of whether that specific entity has been inserted before or not, and I can confirm that the values are always different, no key conflicts as far as the database is concerned. My suspicion is that it has something to do with the temporary entitykey that gets set prior to the actual insert, but I don't know how to confirm that, nor do I know how to resolve it.
My gut feeling is that the solution in the prior question, to set the SaveOptions to None, would not be the best solution. (See prior discussion here)
I've had this issue with my repository using a loop as well and thought that it might be caused by some weird race-like condition. What I've done is refactor out a UnitOfWork class, so that the repository.add() method is strictly adding to the database, but not storing the context. Thus, the repository is only responsible for the collection itself, and every operation on that collection happens in the scope of the unit of work.
The issue there is that: In a loop, you run out of memory damn fast with EF4. So you do need to store the changes periodically, I just don't store after every save.
public class BaseRepository : IRepository where TEntity : class
{
private TxRPEntities txDB;
private ObjectSet _objectSet;
public void Add(TEntity entity)
{
SetUpdateParams(entity);
_objectSet.AddObject(entity);
}
public void Save()
{
txDB.SaveChanges();
}
Then you can do something like
foreach(Requirement r in requirements)
{
var car = new CustomerAgreementRequirement();
car.CustomerAgreementId = viewModel.Agreement.CustomerAgreementId;
car.RequirementId = r.RequirementId;
_carRepo.Add(car); //Save new record
if (some number limiting condition if you have thousands)
_carRepo.Save(); // To save periodically and clear memory
}
_carRepo.Save();
Note: I don't really like this solution, but I hunted around to try to find why things break in a loop when they work elsewhere, and that's the best I came up with.
We have had some odd collision issues if the entity is not added to the context directly after being created (before doing any assignments). The only time I've noticed the issue is when adding objects in a loop.
Try adding the newed up entity to the context, do the assignments, then save the context. Also, you don't need to save the context each time you add a new entity unless you absolutely need the primary key.

Resources