SaveChangesAsync fails and callback not executed (IdeaBlade) - entity-framework

I inherited an application built on Silverlight 4 using IdeaBlade from DevForce 2010 version 6.1.15.0. The backend database is SQL Server 2008. In tracking down some updates/inserts that weren't working, I discovered that I was getting the following error from the call to IdeaBlade.EntityModel.EntityManager.SaveChangesAsync().
"An entity or entities containing a referenced temporary Id is missing from the list of entities provided. Missing entities include: Revision: -100. See exception members for more details"
Additional digging uncovered the following additional information.
"The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_valuation_revision". The conflict occurred in database "XX", table "Revision", column 'RevisionID'. The statement has been terminated."
In digging deeper, I discovered the following:
The two main classes involved are: Valuation and Revision
Valuation has a collection of Revisions. On the database, this is represented by a FK on the Revision table (not null) that references the PK of the Valuation table.
In addition, Valuation has a reference to the CurrentRevision. This is represented on the database as a FK on the Valuation table (can be null if there are no revisions) that references the PK on the Revision table.
In the problem code, a new Revision object is created like so:
myNewRevision = new Revision { Valuation = myExistingValuation };
At this point, myNewRevision is an IdeaBlade.EntityModel.Entity with an EntityState of "Added".
After a couple of additional changes to the object, the following code is executed.
EntityManager.AddEntity(myNewRevision);
The above call to AddEntity doesn't seem to accomplish anything as the state of MyNewRevision is the same after the call as it is before the call.
Next an attempt is made to save the newly created Revision object.
SaveChangesAsync(new[] { myNewRevision }, null, RevisionCallback, null);
The above statement is within a try...catch block and no error is thrown, but the RevisionCallback routine is never executed. Also the EntityState of myNewRevision is still "Added" and the key is still showing as -100.
Then the existing Valuation object is updated with the reference to the new Revision.
myExistingValuation.CurrentRevision = myNewRevision;
This causes the EntityState of myExistingValuation to change from "Unchanged" to "Modified".
Finally, the code tries to save the changes to the Valuation object.
SaveChangesAsync(new[] { myExistingValuation }, null, ValuationCallback, null);
The result of this call is that the ValuationCallback is called, but myExistingValuation is still showing as "Modified", the reference to myExistingValuation.CurrentRevision still refers to myNewRevision with a key of -100 and an EntityState of "Added". The EntitySaveOperation object returned to the callback method has Exception == null, HasError = true, and the error is the one described at the beginning of this question. That is,
"An entity or entities containing a referenced temporary Id is missing from the list of entities provided. Missing entities include: Revision: -100. See exception members for more details"
and
"The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_valuation_revision". The conflict occurred in database "XX", table "Revision", column 'RevisionID'. The statement has been terminated."
The database is not updated.
Any suggestions? Why doesn't the callback routine from the first call to SaveChangesAsync get executed? How can I get the updates to work?

It's odd that the RevisionCallback isn't called, but it's possible the async method hasn't completed when you do the next steps.
You can't use try/catch with the async methods here, as DevForce 2010 doesn't use the newer task-based async pattern; so exceptions will be returned to your callback or completion handler, or possibly to an EntityServerError handler if one is set up for the EntityManager.
DevForce will by default save all changes to entities in cache unless you specify a save list, but that list doesn't have to contain only one item. When you do a save of entities with temporary ids, all entities with a temporary PK or FK need to be in the save list passed to SaveChangesAsync. DevForce will automatically handle the fixup to the FK after the save completes successfully.
Is there any reason you can't do something like the following instead:
myNewRevision = new Revision { Valuation = myExistingValuation };
myExistingValuation.CurrentRevision = myNewRevision;
SaveChangesAsync(ValuationCallback);

Related

"Cannot insert explicit value for identity column in table 'x' when IDENTITY_INSERT is set to OFF" - inserting record with nested custom object

I get the error "Cannot insert explicit value for identity column in table 'UserPermission' when IDENTITY_INSERT is set to OFF" trying to insert a record as follows:
dbContext.User.Add(someUser);
dbContext.SaveChanges();
That being said, the User file has the custom class UserPermission as one of its parameters, and someUser's UserPermission is not null and has a set ID parameter. Why does this happen and is it possible to avoid getting this error without having to explicitly add a UserPermissionID foreign key parameter in my User model and setting the UserPermission parameter to null?
Thanks in advance.
This issue typically happens when deserializing entities that have related entities in the object graph then attempting to add them. UserPermission is likely an existing record that in the DB is set up with an identity PK, but EF doesn't appear to recognize that in the entity definition. (I.e. set to DatabaseGenerated(DatabaseGeneratedOption.Identity). If it had been you would most likely be seeing a different problem where a completely new duplicate UserPermission was being created.
If someUser, and it's associated someUser.UserPermission are deserialized entities then you need to do a bit of work to ensure EF is aware that UserPermission is an existing row:
void AddUser(User someUser)
{
var existingPermission = _context.UserPermissions.Local
.SingleOrDefault(x => x.UserPermissionId == someUser.UserPermission.UserPermissionId);
if (existingPermission != null)
someUser.UserPermission = existingPermission;
else
_context.Attach(someUser.UserPermission);
_context.Users.Add(someUser);
_context.SaveChanges();
}
In a nutshell, when working with detached entities that a DbContext may not be tracking, we need to check the Local state for any existing tracked instance for that ID. If we find one, we substitute the detached reference for the tracked one. If we don't find one, we attach the detached one before Adding our user.
This still isn't entirely safe because it assumes that the referenced UserPermission will exist in the database. If for any reason a non-existent UserPermission is sent in (row deleted, or fake data) you will get an exception on Save.
Passing detached entity references around can seem like a simple option at first, but you need to do this for every reference within a detached entity. If you simply call Attach without first checking, it will likely work until you come across a scenario where at runtime it doesn't work because the context happens to already be tracking an instance.

Entity Framework Update Fails for Entity With ID of 0

Using Entity Framework Core 3.0 and facing an odd issue where EF will throw an exception if I try to update an entity with ID=0, it thinks that ID=0 is a temporary value. Same code updates entity with ID=1 or higher without any problems.
Exception:
InvalidOperationException: The property 'Id' on entity type 'MyType' has a temporary value
while attempting to change the entity's state to 'Modified'. Either
set a permanent value explicitly or ensure that the database is
configured to generate values for this property.
Exception is triggered on the following statement:
_context.Attach(MyType).State = EntityState.Modified;
I don't want to reseed all my tables to start with 1.
Is this expected behavior with EF? It should be possible to save entities with ID=0.
Any advice on how to resolve this?
Thanks.
You have to do something about this zero ID value. It's a ticking time bomb.
You'll always have to be on your guard because it definitely is expected behaviour. EF has inner logic depending on key default values. In EF6 you could do this because it was less refined. (In this area that is).
Let me show you how leaving this ID value can backfire on you in the future.
You have this MyType object. Let's call it entity to follow some naming conventions/habits. Its ID value is 0 and it's not attached to the context.
Now suppose you don't use this rather redundant way to attach it as modified but the new EF-core way:
context.Update(entity);
Now you won't see any exception, but no, the issue isn't fixed. It's gotten worse. The entity object's state is Added now and you're going to add a new record to your table. That might even go unnoticed for a while, adding to the pile of mess you have to clean up later.
Had its ID value been > 0, EF's Update method would have concluded it's an existing entity and its state should be Modified.
You can set entity's state to Modified if (1) it's not attached and (2) you use...
context.Entry(entity).State = EntityState.Modified;
And that's another time bomb. Later code changes remove the first condition (it's not attached) and boom.

Unable to re-insert after unique key violation

I have the following scenario:
A business logic function that uses EF Core2 checks if a record already exists. If the record does not exists, it is inserted on the database.
I have multiple threads calling this business logic function. What happens is:
If the function is called simultaneous with the same parameters, both instances checks if the record exists - and it does not exists. So both instances inserts the same record.
When context.SaveChanges() is called on the first instance, all goes ok. But the second SaveChanges() throws an exception because the record already exists (there is an unique index on the database).
If i catch that exception and try to insert with new value for UNIQUE_KEY, still it's throwing an exception since previously added entity still in track.
How can I implement this to avoid the exception?
Instead of simply adding a new entity inside your catch-block, you could modify the existing entity that caused the exception.
foreach (var entry in _dbContext.ChangeTracker.Entries<YourEntity>().Where(e => e.State == EntityState.Added)))
{
entry.Entity.YourProperty = newvalue;
}
Just iterate through all your entities of given type and update your property that has a unique constraint.

Store update insert or delete statement affected an unexpected number of rows (0)

I'm using the code show below to update an entity model. But I get this error:
Store update, insert, or delete statement affected an unexpected number of rows (0)
And reason of this error is also known, it's because the property does not exist in the database. For that I found have only one option: first check that the entity exists, then update it.
But, as I'm updating 10,000+ rows at a time, it will be time consuming to check each time in database if this property exist or not.
Is there any another way to solve this ?
Thank you.
foreach (Property item in listProperties)
{
db.Properties.Attach(item);
db.Entry(item).Property(x => x.pState).IsModified = true;
}
db.SaveChanges();
You use it the wrong way. If you want to update without retrieving the entity, just change the state of the updated entity with providing the id.
foreach (Property item in listProperties)
{
db.Entry(item).State = EntityState.Modified;
}
db.SaveChanges();
Attaching an existing but modified entity to the context
If you have an entity that you know already exists in the database but
to which changes may have been made then you can tell the context to
attach the entity and set its state to Modified.
When you change the state to Modified all the properties of the entity
will be marked as modified and all the property values will be sent to
the database when SaveChanges is called.
Source
I got this error Just by updating EF version from 5 to 6 solved the issue.

entity framework update foreign key throws exception if AutoDetectChangesEnabled set false

I have table A and B
A like : id
name
bid
B like : id
type
in table A has a data record reference with B1,now I want update A reference with B2.
in my unitofwork if I set AutoDetectChangesEnabled = true it's work ok, but I set AutoDetectChangesEnabled = false reason is I want to up speed throw the exception like this:
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: A referential integrity constraint violation occurred: The property value(s) of 'GoodsKind.goods_kind_id' on one end of a relationship do not match the property value(s) of 'EnrolmentType.goods_kind' on the other end."
how cand i do?
I just had this error as well. The problem for me was that I have a complex type. When I changed the master record (let's say Person) I also wanted to change the complex type List with his contact information(s). So when I tried to save them both in one screen I got this error. Check if you fill all the ids on your screen for the master record and the complex type records. Check if they are posted to the server (if you use in example MVC). You can do this by checking the Bind statement by your MVC action.
The error says that the ID and the object specified doesn't match. This means that you are saying that A has a B with ID=2 but at the same time you have A with an object of type B with ID=5. Because you are working in a disconnected environment, EF doesn't know which one is the correct. To solve this issue you can do one of the following things:
-Get the object from EF, modify it and then save (connected environment).
OR
-Update the IDs manually (update A setting ID=5 because the object B has ID=5).
Always remember that EF tracks changes if it is in a connected environment and the tracking is enabled. If not, it has no clue of the changes you made until it tries to save it in the DB (where the object is compared with the values in the DB). One thing that you can do to manually tell EF that the entity has been modified in a disconnected environment is:
dbContext.Entry(objectA).State = EntityState.Modified;