ASP.NET Core EF DbUpdateConcurrencyException - entity-framework

I was trying to manually implement a cascade delete in my ASP.NET Core website.
So, I got a Poll section which is based on 3 entities: PollQuestion, PollOption, PollAnswer.
I'm starting this by deleting all the answers, and this goes fine, then I move to deleting the options with this code:
if (answersDeleted) {
options = GetOptionsList(_pollID);
context.PollOption.RemoveRange(options);
context.SaveChanges();
return true;
} else {
return false;
}
When the SaveChanges() is executed I get this exception:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 2 row(s). Data may have been modified or deleted since entities were loaded.
So. I can't really understand why he was expecting to affect 1 row since I pass a list to RemoveRange().

In case someone could make use of this one day.... i found the problem, it wasn't strictly related to the chunk of code i posted; few days ago i modified the poll table, and changed the Key, from a single field key to a composite key, then i forgot to reflect that change in my Model, which still was using a single key. It wasn't returning any error so i didn't notice this until i had to do other stuff on the DB and there i found the problem.

Related

Entity Framework delete without select and Optimistic Concurrency

I am trying to delete a record if it exists using Entity Framework - I want to generate something like
delete from tbl where id = 1
I don't care whether any records exist, I just want to delete them if they do. I should be able to do with without selecting them first like this:
var record = new tbl { id = 1, rel = new tbl_related { tbl_id = 1 } };
context.tbl.Attach(tbl);
context.tbl.Remove(tbl);
context.SaveChanges();
The syntax might not exactly be there because this isn't a copy and paste, and I've added the foreign key relationship to show that I'm taking this into account.
On running this code the correct SQL is generated by EF and run but I get a System.Data.Entity.Infrastructure.DbUpdateConcurrencyException saying "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded".
However, I'm fully expecting zero rows to be deleted a lot of the time. Any ideas on how I can get around this?
I've been pointed to How to ignore a DbUpdateConcurrencyException when deleting an entity as a possible duplicate. This suggests handling the DbUpdateConcurrencyException which I am now doing (and ignoring), but that post talks about trying to delete rows that might have been deleted - I want to delete rows that might never have existed. I know this might just be a question of semantics because the resolution is the same but I want to highlight that I am doing something that I see as perfectly reasonable and the Framework is not handling it very well.
Thanks
There is an extension for EF on github that allows you to update and delete entities with a single call to the database.
With this extension you can do something like this:
context.tbl.Delete(t => t.Id == 1);
A single call should eliminate your DbUpdateConcurrencyException.

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 6 Trigger values not in object after SaveChanges()

How can I ensure that EF 6 retrieves the changes to a record that are made after a trigger executes?
My issue is that I have a trigger on my table that generates column values after insert. When I add my new object and call SaveChanges(), I want to turn around and get that object back out with the updated values. I've found some similar questions here, but they are old and suggest using things like context.Refresh() that do not exist now. In one case there was a suggestion to use:
context.Configuration.AutoDetectChangesEnabled = true;
This hasn't helped either.
Thanks for your help!

Entity Framework object graph deletion with Breeze

I am encountering a recurring problem that just makes no sense, and hoping someone (in the Breeze team?) can shed some light.
The following model illustrates the entities in question.
As you can see, I'm adhering pretty strictly to Entity Framework conventions in my property names, and as a result, if I check in SQL the cascade on delete rules are set by EF code first when it creates the db.
Now, when I try to delete a BusUnit manually in SQL, the delete cascades correctly and the corresponding BusUnitDimensions are also deleted, as it should be. Likewise, if I delete a Dimension in SQL, the corresponding BusUnitDimensions are also deleted.
However, in my application, if I mark a BusUnit as setDeleted with Breeze and then try saveChanges, I get the following error.
The operation failed: The relationship could not be changed because one
or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null
value. If the foreign-key does not support null values, a new relationship
must be defined, the foreign-key property must be assigned another
non-null value, or the unrelated object must be deleted.
Strangely though, if I mark a Dimension for deletion and then save (within Breeze), the cascaded delete works correctly and both the Dimension and its corresponding BusUnitDimensions are deleted.
So, why the inconsistency? Why are the cascaded delete rules in SQL not being applied for BusUnits but yet they're working for Dimensions? I've read elsewhere that Breeze does not support cascaded deletes, but then why is my Dimensions case working?
EDIT:
I've removed my previous edits as they weren't relevant. The changes below follow on from Ward's answer...
My model now looks like this, and BusUnitDims now uses BusUnitId and DimId as a compound key, and I've added a bool, IsBud for the purposes of payload.
I haven't yet implemented deletes for BusUnits, but already if I try delete a Dim, I'm getting the same error message:
The operation failed: The relationship could not be changed because one
or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null
value. If the foreign-key does not support null values, a new relationship
must be defined, the foreign-key property must be assigned another
non-null value, or the unrelated object must be deleted.
I have noticed that cascaded deletes is no longer enabled, and in fact, to get EF to build the database I to add the following configuration:
modelBuilder.Entity<BusUnitDim>()
.HasRequired(bud => bud.BusUnit)
.WithMany(bu => bu.BusUnitDims)
.HasForeignKey(bud => bud.BusUnitId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<BusUnitDim>()
.HasRequired(bud => bud.Dim)
.WithMany(d => d.BusUnitDims)
.HasForeignKey(bud => bud.DimId)
.WillCascadeOnDelete(false);
So, with cascading now explicitly not in place, I can understand why the error occurs. Does that imply that in the controller, one has to specifically mark each map for deletion when deleting a parent Dim or BusUnit and before saveChanges is called, or is there some way to configure EF to take advantage of cascaded deletes as this would hugely simplify the code in my controller?
(PS: it gets even more complex, because BusUnitDims ends up having a further join table of its own, MetricBusUnitDims to accommodate yet another entity in the model and their relationships. This is why I'm trying to get the principles right early on)
EDIT: (A CONTROLLER SOLUTION FOR BUSUNITS)
So, the following approach works for BusUnits:
function deleteBusUnit(busUnitVm) { // note that you pass in the item viewmodel, not the entity
var busUnit = busUnitVm.busUnit;
var mapVms = busUnitVm.dimMapVms;
var dimHash = createBusUnitDimHash(busUnit);
mapVms.forEach(function (mapVm) {
var map = dimHash[mapVm.dim.id];
if (map) {
datacontext.markDeleted(map);
}
});
datacontext.markDeleted(busUnit);
save().then(function() { getDBoardConfig(); });
}
}
Is this the correct approach? if so, I'll still have to figure out the following:
How to approach Dims. These are different becuase the item viewmodel is defined for BusUnits.
How to approach the situation where there is a join tabel one level down, e.g. MetricBusUnitDIm.
EDIT: (A CONTROLLER SOLUTION FOR DIMS)
function deleteDim(dim) {
return bsDialog.deleteDialog(dim.name, true)
.then(function () {
vm.busUnitVms.forEach(function (busUnitVm) {
busUnitVm.busUnit.busUnitDims.forEach(function (bud) {
if (bud.dimId === dim.id) {
datacontext.markDeleted(bud);
}
});
});
datacontext.markDeleted(dim);
save().then(function () { getDboardConfig(); });
});
}
I believe your problems are traceable to the fact that your mapping table BusUnitDimension has its own primary key, Id, as opposed to the more typical approach in which the BusUnitId and DimensionId FK properties together comprise the compound primary key of BusUnitDimension.
Observe that OrderDetails in Northwind and the HeroPoweMap in the Breeze many-to-many example have compound keys.
Your choice creates complications.
First, it becomes possible to create multiple BusUnitDimension entities representing the same association between BusUnit and Dimension (i.e., they all have the same pair of FKs). The database may be able to prevent this (it's been a long time since I looked) but whether it does or doesn't, it won't prevent you from creating those duplicates in Breeze ... and maybe not in EF either.
Secondly, it opens you up to the problem you're currently facing. If those mapping entities are in the DbContext when you perform the delete, EF may (apparently does) try to null their FK properties as it sets either BusUnit or Dimension to the deleted state.
You can get around this, as has been suggested, by making both the BusUnitId and DimensionId FK properties nullable. But that is contrary to the semantics as a BusUnitDimension must link a real BusUnit to a real Dimension; they aren't optional. The practical consequence may be that you don't get cascade delete from the EF perspective if you do this (not sure if the DB will enforce that either). That means you'd have orphaned BusUnitDimension rows in your database with one or both FKs being null. I speculate because I'm not used to getting into this kind of trouble.
Another approach would be to set their FK values to zero (I think Breeze does this for you). Of course this implies the existence of BusUnit and Dimension table rows with Id == 0, if only during the delete operation.
Btw, you could actually have such "sentinel entities" in your DB.
You must make sure that these BusUnitDimension are in the deleted state or EF (and the DB) will either reject them (referential integrity constraint) or orphan them (you'll have BusUnitDimension rows in your database with one or both FKs being zero).
Alternatively, if you know that the DB will cascade delete them, you can simply remove them from the DbContext (remove from the EntityInfoMap in the EFContextProvider). But now you have to tell the Breeze client to get rid of them too if it happens to have them hanging around.
Enough Already!
These wandering thoughts should tell you that you've got yourself in a jam here with way too much bookkeeping ... and all because you gave BusUnitDimension its own Id primary key.
It gets a lot easier if you give BusUnitDimension the compound key, {BusUnitId, DimensionId}. You must also give it a payload property (anything will do) to prevent EF from hiding it in its "many-to-many" implementation because Breeze doesn't handle that. Adding any nonsense property will do the trick.
HTH
That has nothing to do with Breeze.. The originating message is from Entity Framework..
inside BusUnitDimension Model update BusUnitId property to:
public Nullable<int> BusUnitId { get; set; }
Notice the Nullable struct..

Entity framework using old primary keys when inserting new records in database

I have a run into a serious problem several times now using MVC 4 and EF.
The problem is best illustrated by example:
I have records in a DB table with the following PKs, 1,2,3,4.
1 and 2 are deleted. When EF goes to insert a new record, it is assigning it with PK 1 again. The next insert will use 2 and then it will try 3 and get a PK violation.
I saw the same thing yesterday in another table with another insert.
In the following, image you can see that the db.SaveChanges failed when inserting the a new record with PK 3.
As you can see the DB is set to auto-increment from the following image:
Here is my controller action (it is used for inserts and edits - but that should not matter):
if (ModelState.IsValid)
{
//update pricelist
Pricelist pricelist = new Pricelist();
pricelist.InjectFrom(adminEditPricelistVM.Pricelist);
pricelist.PricelistProducts = new List<PricelistProduct>();
pricelist.SubscriberId = (int)UserManagement.getUsersSubscriberId(WebSecurity.GetUserId(System.Web.HttpContext.Current.User.Identity.Name));
if (adminEditPricelistVM.Pricelist.PricelistId != 0)
{
db.Entry(pricelist).State = EntityState.Modified;
}
else
{
db.Pricelists.Add(pricelist);
}
db.SaveChanges();
adminEditPricelistVM.Pricelist.PricelistId = pricelist.PricelistId;
etc...
The only clue I have is that in the seeding config for my data, we are using the following commands to begin the seeding at 1, when replacing the data:
context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('Features', RESEED, 1)");
Perhaps this has something to do with it, but I doubt it - since this is not called at that time.
BTW, this is not consistent. I cannot replicate manually. It just seems to happen from time-to-time and when it does, EF will continue to err on each insert attempt until it passes all the used IDs and finds the next free one. In other words, I will get an error inserting on PK 3. Then on the next insert attempt, it will err on PK 4 and then on the next attempt it will succeed because PK 5 was not being used. It's as if, there is a memory of PKs in use somewhere that gets reset.
Any help would be greatly appreciated!
OK. I can confirm that the problem is caused by the DB reseed. This basically sets the count back to 1 regardless of any remaining records in the DB. Weird but true.