ASP.NET API throws Entity Framework error - c#

Below is the code of my API, but it always returns a 500 Internal server error. The exception is thrown when executing SaveChangesAsync().
The instance of entity type 'NewsReport' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values
[HttpPut("{id}")]
public async Task<IActionResult> EditDevblog([FromBody] DevblogModel devblog, int id)
{
if (!ModelState.IsValid)
return BadRequest();
var devblogInDb = _context.DevblogModels
.Include(d => d.Fixs)
.Include(d => d.News)
.Include(d => d.Removes)
.Include(d => d.Updates)
.SingleOrDefault(d => d.Id == id);
if (devblogInDb == null)
return NotFound();
devblogInDb.Fixs = devblog.Fixs;
devblogInDb.News = devblog.News;
devblogInDb.Removes = devblog.Removes;
devblogInDb.Updates = devblog.Updates;
devblogInDb.PatchName = devblog.PatchName;
await _context.SaveChangesAsync();
var h = _context.DevblogModels.SingleOrDefault(d => d.Id == id);
return Ok();
}

Please see my answer here.
EF Core throws this exception when you replace a child entity instance with a new one like you are doing here:
devblogInDb.Fixs = devblog.Fixs;
devblogInDb.News = devblog.News;
devblogInDb.Removes = devblog.Removes;
devblogInDb.Updates = devblog.Updates;
devblogInDb.PatchName = devblog.PatchName;
Unfortunately the only workable alternative has been to update the current child entity properties directly. Per my original answer:
// In your DevblogModels and assuming a 1:1 relationship
public void SetNewsReport(NewsReport newsReport)
{
this.News.UpdateFrom(newsReport);
}
// Then, on your NewsReport entity
internal void UpdateFrom(NewsReport other)
{
this.Title = other.Title;
this.Article = other.Article;
// other properties...
}
The second approach in my original answer suggests to detach the entity, replace the child instance, then reattach and try to save. In my attempts with this approach the exception went away, but the data did not persist to the database. Maybe you'll have better luck with it than I did...
context.Entry(devblogInDb).State = EntityState.Detached;
devblogInDb.Fixs = devblog.Fixs;
devblogInDb.News = devblog.News;
devblogInDb.Removes = devblog.Removes;
devblogInDb.Updates = devblog.Updates;
devblogInDb.PatchName = devblog.PatchName;
context.Entry(devblogInDb).State = EntityState.Modified;
A Bit Further
As my original answer suggests this has been a longstanding issue with EF Core. This issue was supposed to be fixed in EF Core 2.1, however, the OP of my original answer was using the release candidate for EF Core 2.1. Apparently this issue either didn't make the cut or the fixes put in place still have a few bugs in them (it is just a release candidate after all...)
For a great read on this topic Julie Lerman posted an article in the MSDN magazine that you can find here. She also has a few alternative solutions that you could use as well.

Related

EF Core failing to save modified entity

I'm working on an application that's using EF Core (version 5.0.5) with a MySql database. My context is setup so that QueryTrackingBehavior is NoTracking and when i try to save a modified entity I get the following exception:
Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See ...
The application uses the Repository/UnitOfWork pattern but the code essentially boils down to:
public class MyEntity
{
public Guid Id {get; set;}
public int Counter {get; set;}
}
public async Task UpdateCounter(Guid id)
{
DbContextOptionsBuilder<DatabaseContext> builder = new DbContextOptionsBuilder<DatabaseContext>()
.UseMySql(CONNECTION_STRING, ServerVersion.AutoDetect(CONNECTION_STRING), options =>
{
options.UseNetTopologySuite();
})
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
MyDbContext context = new MyDbContext(builder.Options);
MyEntity entity = await context.Set<MyEntity>().FirstOrDefaultAsync(e => e.Id == id);
entity.Counter += 1;
context.Update(entity);
await context.SaveChangesAsync();
}
As a test, I stored the result of context.Update(entity); to a variable so I could inspect it in debug and it looks correct (as far as I understand), the state says it's modified but yet I still get the exception.
I did some reading online and found a few suggestions and tried replacing context.Update(entity); with:
context.Attach(entity).State = EntityState.Modified;
Which had the same result. I also tried:
context.Entry(entity).State = EntityState.Modified;
Which also had the same result. In my reading I was a little confused what the difference between these three lines even were.
My main question is: Why is EF Core failing to save my entity?
Edit
A couple pieces of information that might be important:
MyEntity.Counter is set as a concurrency token and has a default value
Adding a new entity to the database works no problem, it's just the update that is causing the exception

Failure to attach a detached entity (entity with the same key is already in the context)

I'm using Entity Framework 6, Code First approach. I'll try to present my problem with a simple piece of code:
public void ViewEntity(MyEntity Entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
Db.MyEntities.Attach(Entity); // Exception
}
}
The exception message is: Attaching an entity of type 'MyProgram.MyEntity' failed because another entity of the same type already has the same primary key value.
From what I've read on MSDN it's an expected behaviour. But what I want on that last line is to first check if there is an entity with the same key already attached to a context; if it is, use it instead, and only otherwise attach my entity to context.
But I've failed to find a way to do so. There are many utility methods on ObjectContext instance (for example GetObjectByKey). I can't test them all 'cause they all ultimately need a qualifiedEntitySetName, and I don't have any in my real imlpementation, because this method should be on an abstract class and it should work for all entity types. Calling Db.Entity(this) is no use, there is no EntityKey which would have EntitySetName.
So all of this became complex really fast. And in my terms I just want to check if the object is already in "cache" (context), use it, otherwise use my object and attach it to this context.
To be clear, I have a detached object from a TreeNode.Tag in the first place, and I just want to use it again, or if it's impossible; if there already is one in the context), use that one instead. Maybe I'm missing some crucial concepts of EF6, I'm just starting out with EF.
I've found a solution for me. As I guessed correctly ObjectContext.GetObjectByKey method does what I need, but first I needed to construct qualifiedEntitySetName, and I found a way to do so. A tad bit cumbersome (using reflection, iterating properties of MyDbContext), but does not compare to a headache of a problem I made out of all this. Just in case, here's the patch of code that is a solution for me:
public SdsAbstractObject GetAttachedToContext()
{
var ObjContext = (SdsDbContext.Current as IObjectContextAdapter).ObjectContext;
var ExistingItem = ObjContext.GetObjectByKey(GetEntityKey()) as SdsAbstractObject;
if (ExistingItem != null)
return ExistingItem;
else
{
DbSet.Attach(this);
return this;
}
}
public EntityKey GetEntityKey()
{
string DbSetName = "";
foreach (var Prop in typeof(SdsDbContext).GetProperties())
{
if (Prop.PropertyType.IsGenericType
&& Prop.PropertyType.GenericTypeArguments[0] == ObjectContext.GetObjectType(GetType()))
DbSetName = Prop.Name;
}
if (String.IsNullOrWhiteSpace(DbSetName))
return null;
else
return new EntityKey("SdsDbContext." + DbSetName, "Id", Id);
}
An Entity can be in one of five stages : Added, Unchanged, Modified, Deleted, Detached.
public void ViewEntity(MyEntity entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
// Set the Modified state of entity or you can write defensive code
// to check it before set the state.
if (Db.Entry(entity).State == EntityState.Modified) {
Db.Entry(entity).State = EntityState.Modified
}
// Attached it
Db.MyEntities.Attach(Entity);
Db.SaveChanges();
}
}
Since EF doesn't know which properties are different from those in the database, it will update them all.

How to update entity?

I had a question more detailed earlier which I had no answer, I will have the same question with a simpler way:
I have an EF database with foreign key to another table.
I would like to UPDATE an ENTITY. But I need to this like this and I'll write the codes below:
Go to database and retrieve the Member by id, return EF Member object
Do some changes on the object OUTSIDE the EF Context
Send the MODIFED EF Member into a Save method
In BL layer save method uses the context and save changes.
1)
MemberManager currentMemberManager = new MemberManager();
Member NewMember = currentMemberManager.GetById(2);
2)
NewMember.FirstName = "NewFirstName";
NewMember.LanguageId = 1;
3)
currentMemberManager.Save(NewMember);
4)
public void Save2(Member newMember)
{
using (var Context = new NoxonEntities())
{
Member existingMember = Context.Member.First(c => c.Id == newMember.Id);
existingMember.FirstName = newMember.FirstName;
existingMember.Language = Context.Language.First(c => c.Id == newMember.LanguageId);
Context.SaveChanges();//In here I get the error below
}
}
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 values that
define the referential constraints are not consistent between
principal and dependent objects in the relationship.
Note: You may suggest to SEND a different class (Ex: public class
MyMember) that has all the necessary properties and totally separated
from EF. But this requires much work to get all EF object converting
into my separate classes. Am I right?
I am hoping there is a way to Detach the entity just long enough for me to modify it and save the values into database. (Also, I tried the Detach method which updates no rows at all)
I've been trying to solve this for hours now.
Please, help me to understand it better, I really need a solution. Thank you so much to anyone how has some ideas.
Could you do something simple like detaching the entity, then attaching it to the context when you're ready to save?
MemberManager currentMemberManager = new MemberManager();
Member NewMember = currentMemberManager.GetById(2);
The get:
public Member GetById(int id)
{
var member = YourContext.Members.FirstOrDefault(m => m.id == id);
YourContext.Detach(member);
return member;
}
The save:
public void Save2(Member newMember)
{
using (var Context = new NoxonEntities())
{
Context.Attach(newMember);
Context.ObjectStateManager.ChangeObjectState(newMember, EntityState.Modified);
Context.SaveChanges();
}
}

How to save an updated many-to-many collection on detached Entity Framework 4.1 POCO entity

For the last few days I'm trying to properly update my POCO entities. More specific, it's many-to-many relationship collections.
I've three database tables:
Author - 1..n - AuthorBooks - n..1 - Books.
Translates to two POCO entities:
An Author entity with a Books collection and Book entity with a Authors collection.
Case
When I have one active DbContext, retrieve a Book entity, add an Author and call SaveChanges(), the changes are properly send to the database. All fine so far.
However I've a desktop application with limited DbContext lifetime, as displayed in code fragments below.
public Book GetBook(int id)
{
using (var context = new LibariesContext())
{
return context.Books
.Include(b => b.Authors)
.AsNoTracking()
.Single(b => b.BookId == id);
}
}
public Author GetAuthor(int id)
{
using (var context = new LibariesContext())
{
return context.Authors
.AsNoTracking()
.Single(a => a.AuthorId == id);
}
}
A simplified example of various of my business logic methods, wraps it together:
public void BusinessLogicMethods()
{
Book book = GetBook(id: 1);
Author author = GetAuthor(id: 1);
book.Name = "New book title";
book.Authors.Add(author);
SaveBook(book);
}
public void SaveBook(Book book)
{
using (var context = new LibariesContext())
{
context.Entry(book).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
}
Unfortunately the only thing that is really saved here, is the name of the book. The new author was not saved, neither was an Exception thrown.
Questions
What's the best way to save the collection of a detached entity?
Any workaround for this issue?
I'm new to EF 4.1 too, and if I understand your question correctly, I think I ran into this nonsense over the weekend. After trying every approach under the sun to get the entries to update, I found a mantra here on SO (I can't find it any more) and turned it into a generic extension method. So now, after the line:
book.Authors.Add(author);
I would add the line:
context.UpdateManyToMany(book, b => b.Authors)
You might need to restructure your code to make this happen.
Anyway... here's the extension method I wrote. Let me know if it works (no guarantees!!!)
public static void UpdateManyToMany<TSingle, TMany>(
this DbContext ctx,
TSingle localItem,
Func<TSingle, ICollection<TMany>> collectionSelector)
where TSingle : class
where TMany : class
{
DbSet<TSingle> localItemDbSet = ctx.Set(typeof(TSingle)).Cast<TSingle>();
DbSet<TMany> manyItemDbSet = ctx.Set(typeof(TMany)).Cast<TMany>();
ObjectContext objectContext = ((IObjectContextAdapter) ctx).ObjectContext;
ObjectSet<TSingle> tempSet = objectContext.CreateObjectSet<TSingle>();
IEnumerable<string> localItemKeyNames = tempSet.EntitySet.ElementType.KeyMembers.Select(k => k.Name);
var localItemKeysArray = localItemKeyNames.Select(kn => typeof(TSingle).GetProperty(kn).GetValue(localItem, null));
localItemDbSet.Load();
TSingle dbVerOfLocalItem = localItemDbSet.Find(localItemKeysArray.ToArray());
IEnumerable<TMany> localCol = collectionSelector(localItem)?? Enumerable.Empty<TMany>();
ICollection<TMany> dbColl = collectionSelector(dbVerOfLocalItem);
dbColl.Clear();
ObjectSet<TMany> tempSet1 = objectContext.CreateObjectSet<TMany>();
IEnumerable<string> collectionKeyNames = tempSet1.EntitySet.ElementType.KeyMembers.Select(k => k.Name);
var selectedDbCats = localCol
.Select(c => collectionKeyNames.Select(kn => typeof (TMany).GetProperty(kn).GetValue(c, null)).ToArray())
.Select(manyItemDbSet.Find);
foreach (TMany xx in selectedDbCats)
{
dbColl.Add(xx);
}
ctx.Entry(dbVerOfLocalItem).CurrentValues.SetValues(localItem);
}
I came across this question when I was attempting to solve the same problem. In the end I took a different approach which seems to be working. I had to end up exposing a "state" property on the entities and have the calling context set the state of the entities within the object graph.
This reduces the overhead on the web service/data context side to have to determine what's changed, given that the graph could be populated by any number of query permeation. There's also a NuGet package called GraphDiff which might work for you as well(details in the link below).
Anyhow, full details here: http://sanderstechnology.com/2013/solving-the-detached-many-to-many-problem-with-the-entity-framework/12505/

OptimisticConcurrencyException Does Not Work in Entity Framework In Certain Situations

UPDATE (2010-12-21): Completely rewrote this question based on tests that I've been doing. Also, this used to be a POCO specific question, but it turns out that my question isn't necessarily POCO specific.
I'm using Entity Framework and I've got a timestamp column in my database table that should be used to track changes for optimistic concurrency. I've set the concurrency mode for this property in the Entity Designer to "Fixed" and I'm getting inconsistent results. Here are a couple of simplified scenarios that demonstrate that concurrency checking works in one scenario but not in another.
Successfully throws OptimisticConcurrencyException:
If I attach a disconnected entity, then SaveChanges will throw an OptimisticConcurrencyException if there is a timestamp conflict:
[HttpPost]
public ActionResult Index(Person person) {
_context.People.Attach(person);
var state = _context.ObjectStateManager.GetObjectStateEntry(person);
state.ChangeState(System.Data.EntityState.Modified);
_context.SaveChanges();
return RedirectToAction("Index");
}
Does not throw OptimisticConcurrencyException:
On the other hand, if I retrieve a new copy of my entity from the database and I do a partial update on some fields, and then call SaveChanges(), then even though there is a timestamp conflict, I don't get an OptimisticConcurrencyException:
[HttpPost]
public ActionResult Index(Person person) {
var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
currentPerson.Name = person.Name;
// currentPerson.VerColm == [0,0,0,0,0,0,15,167]
// person.VerColm == [0,0,0,0,0,0,15,166]
currentPerson.VerColm = person.VerColm;
// in POCO, currentPerson.VerColm == [0,0,0,0,0,0,15,166]
// in non-POCO, currentPerson.VerColm doesn't change and is still [0,0,0,0,0,0,15,167]
_context.SaveChanges();
return RedirectToAction("Index");
}
Based on SQL Profiler, it looks like Entity Framework is ignoring the new VerColm (which is the timestamp property) and instead using the originally loaded VerColm. Because of this, it will never throw an OptimisticConcurrencyException.
UPDATE: Adding additional info per Jan's request:
Note that I also added comments to the above code to coincide with what I see in my controller action while working through this example.
This is the value of the VerColm in my DataBase prior to the update: 0x0000000000000FA7
Here is what SQL Profiler shows when doing the update:
exec sp_executesql N'update [dbo].[People]
set [Name] = #0
where (([Id] = #1) and ([VerColm] = #2))
select [VerColm]
from [dbo].[People]
where ##ROWCOUNT > 0 and [Id] = #1',N'#0 nvarchar(50),#1 int,#2 binary(8)',#0=N'hello',#1=1,#2=0x0000000000000FA7
Note that #2 should have been 0x0000000000000FA6, but it's 0x0000000000000FA7
Here is the VerColm in my DataBase after the update: 0x0000000000000FA8
Does anyone know how I can work around this problem? I'd like Entity Framework to throw an exception when I update an existing entity and there's a timestamp conflict.
Thanks
Explanation
The reason why you aren't getting the expected OptimisticConcurrencyException on your second code example is due to the manner EF checks concurrency:
When you retrieve entities by querying your db, EF remembers the value of all with ConcurrencyMode.Fixed marked properties by the time of querying as the original, unmodified values.
Then you change some properties (including the Fixed marked ones) and call SaveChanges() on your DataContext.
EF checks for concurrent updates by comparing the current values of all Fixed marked db columns with the original, unmodified values of the Fixed marked properties.
The key point here is that EF treats the update of you timestamp property as a normal data property update. The behavior you see is by design.
Solution/Workaround
To workaround you have the following options:
Use your first approach: Don't requery the db for your entity but Attach the recreated entity to your context.
Fake your timestamp value to be the current db value, so that the EF concurrency check uses your supplied value like shown below (see also this answer on a similar question):
var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
currentPerson.VerColm = person.VerColm; // set timestamp value
var ose = _context.ObjectStateManager.GetObjectStateEntry(currentPerson);
ose.AcceptChanges(); // pretend object is unchanged
currentPerson.Name = person.Name; // assign other data properties
_context.SaveChanges();
You can check for concurrency yourself by comparing your timestamp value to the requeried timestamp value:
var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
if (currentPerson.VerColm != person.VerColm)
{
throw new OptimisticConcurrencyException();
}
currentPerson.Name = person.Name; // assign other data properties
_context.SaveChanges();
Here is another approach that is a bit more generic and fits in the data layer:
// if any timestamps have changed, throw concurrency exception
var changed = this.ChangeTracker.Entries<>()
.Any(x => !x.CurrentValues.GetValue<byte[]>("Timestamp").SequenceEqual(
x.OriginalValues.GetValue<byte[]>("Timestamp")));
if (changed) throw new OptimisticConcurrencyException();
this.SaveChanges();
It just checks to see if the TimeStamp has changed and throws concurrency exception.
If it's EF Code first, then use code similar to below code. This will change the original TimeStamp loaded from db to the one from UI and will ensure OptimisticConcurrencyEception occurs.
db.Entry(request).OriginalValues["Timestamp"] = TimeStamp;
I have modified #JarrettV solution to work with Entity Framework Core. Right now it is iterating through all modified entries in context and looking for any mismatch in property marked as concurrency token. Works for TimeStamp (RowVersion) as well:
private void ThrowIfInvalidConcurrencyToken()
{
foreach (var entry in _context.ChangeTracker.Entries())
{
if (entry.State == EntityState.Unchanged) continue;
foreach (var entryProperty in entry.Properties)
{
if (!entryProperty.IsModified || !entryProperty.Metadata.IsConcurrencyToken) continue;
if (entryProperty.OriginalValue != entryProperty.CurrentValue)
{
throw new DbUpdateConcurrencyException(
$"Entity {entry.Metadata.Name} has been modified by another process",
new List<IUpdateEntry>()
{
entry.GetInfrastructure()
});
}
}
}
}
And we need only to invoke this method before we save changes in EF context:
public async Task SaveChangesAsync(CancellationToken cancellationToken)
{
ThrowIfInvalidConcurrencyToken();
await _context.SaveChangesAsync(cancellationToken);
}

Categories

Resources