ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value
Problem SOLVED!
Attach
method could potentially help somebody but it wouldn't help in this situation as the document was already being tracked while being loaded in Edit GET controller function. Attach would throw exactly the same error.
The issue I encounter here was caused by function canUserAccessA()
which loads the A entity before updating the state of object a. This was screwing up the tracked entity and it was changing state of a object to Detached
.
The solution was to amend canUserAccessA()
so that the object I was loading wouldn't be tracked. Function AsNoTracking()
should be called while querying the context.
// User -> Receipt validation
private bool canUserAccessA(int aID)
{
int userID = WebSecurity.GetUserId(User.Identity.Name);
int aFound = db.Model.AsNoTracking().Where(x => x.aID == aID && x.UserID==userID).Count();
return (aFound > 0); //if aFound > 0, then return true, else return false.
}
For some reason I couldnt use .Find(aID)
with AsNoTracking()
but it doesn't really matter as I could achieve the same by changing the query.
Hope this will help anybody with similar problem!
Failed because another entity of the same type already has the same primary key value As
The problem was solved this way.
public bool Edit(WorkshopReport updated)
{
try
{
var local = _ctx.Set<WorkshopReport>()
.Local
.FirstOrDefault(f => f.Id == updated.Id);
if (local != null)
{
_ctx.Entry(local).State = EntityState.Detached;
}
_ctx.Entry(updated).State = System.Data.Entity.EntityState.Modified;
return true;
}
catch (Exception ex)
{
// TODO log this error
return false;
}
}
Attaching an entity of type 'X' failed because another entity of the same type already has the same primary key value. error
The first three solutions did not work. What I was ultimately forced to do to get a solution was follow along with your side note (at least I think that is what you were getting at). The issue was arising because the includes of the GameType and AppplicationUsers' were trying to create new entities of their respective objects instead of finding the information in the database and setting it to modified. Below is the updated controller code that has gotten it working:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(EditMatchesViewModel match)
{
if (ModelState.IsValid)
{
var updatedMatch = db.Matches.Find(match.ID);
updatedMatch.Date = match.Date;
updatedMatch.AwayTeam = match.AwayTeam;
updatedMatch.HomeTeam = match.HomeTeam;
updatedMatch.Division = match.Division;
updatedMatch.Games = new List<Game>();
foreach (var game in match.Games)
{
if (game.ID > 0)
{
var updatedGame = db.Games.Find(game.ID);
updatedGame.GameType = db.GameType.Find(game.GameType.ID);
updatedGame.AwayPlayer1 = db.Users.Find(game.AwayPlayer1.Id);
updatedGame.AwayPlayer2 = db.Users.Find(game.AwayPlayer2.Id);
updatedGame.HomePlayer1 = db.Users.Find(game.HomePlayer1.Id);
updatedGame.HomePlayer2 = db.Users.Find(game.HomePlayer2.Id);
updatedGame.AwayScore1 = game.AwayScore1;
updatedGame.AwayScore2 = game.AwayScore2;
updatedGame.HomeScore1 = game.HomeScore1;
updatedGame.HomeScore2 = game.HomeScore2;
updatedMatch.Games.Add(updatedGame);
}
}
db.Matches.Attach(updatedMatch);
db.Entry(updatedMatch).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(match);
}
Attaching an entity of type failed because another entity of the same type already has the same primary key value
It's hard to be sure as the code you've shared appears to be a wrapper built around Entity Framework and so obscures away some of the necessary detail but an educated guess says that you're dealing with Detached Entities
.
The keyword to search for is DbContext
(Database Context).
If you use Entity Framework (EF) to fetch some data from your database this data remains attached to the database or DbContext
, this is an attached entity
.
Any changes made to the data are now automatically tracked by EF so when you call SaveChanges()
it knows to UPDATE
the existing data.
In your case I suspect that _jobQueueUnitOfWork.JobQueueRepository.GetAll();
fetches data from somewhere else such as a Web API. As this data was created outside of DbContext
then EF can't possibly know what state it's in, this is a detached entity
.
The solution is to simply tell EF what state the data is in, in your case it's modified and requires an UPDATE
over an INSERT
.
tempList.ForEach(jq =>
{
context.jobqueue.Attach(jq); // Attach to `DbContext`
context.Entry(jq).State = System.Data.Entity.EntityState.Modified; // Modified
});
If you search for Entity Framework articles relating to dbcontext
, change tracking
and attached/detached entities
it should answer a lot of your questions.
Attaching an entity of type '' failed because another entity has the same primary key value
This is the code that is causing the problem:
var dbEntity = await _context.Set<T>().SingleOrDefaultAsync(x => x.Id == entity.Id);
_context.Set<T>().Attach(entity);
In this scenario, if you find an existing entity, it just got stored in the context. If you try to add one that already exists, EF can't handle that. There are a couple of ways you can get around this. I'll show two:
var dbEntity = await _context.Set<T>().AsNoTracking().SingleOrDefaultAsync(x => x.Id == entity.Id);
In theory, if you don't track it, then you should be able to add the other one.
However, I think this way is better:
bool doesEntityExist = await _context.Set<T>().Any(x => x.Id == entity.Id);
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = doesEntityExist ? EntityState.Modified : EntityState.Added;
Attaching an entity of type 'ModelName failed because another entity of the same type already has the same primary key value
Entity Framework tracks objects you have loaded. So when you query the database for the OldConnector
object, that item is kept in memory.
You then go on to try and save your NewConnector
object which has the same primary key ID. Entity Framework checks it's internal state and finds a matching entity, that is why you get the error. Since you are trying to update the existing object, you should do this instead:
public void Update(Connector newConnector)
{
if (newConnector == null)
{
throw new ArgumentNullException(nameof(newConnector));
}
var oldConnector = _db.ConnectorsTable
.Where(x => x.ID == newConnector.ID)
.Single(); //Grabbing the old connectors password
if (newConnector.Password == "")
{
newConnector.Password = oldConnector.Password;
}
//Update the old entity with values from the new entity:
_db.Entry(oldConnector).CurrentValues.SetValues(newConnector);
_db.SaveChanges();
}
Related Topics
Recursion, Parsing Xml File With Attributes into Treeview C#
When Correctly Use Task.Run and When Just Async-Await
How to Read a CSV File into a .Net Datatable
Easiest Way to Split a String on Newlines in .Net
How to Convert Json to Xml or Xml to Json
Filesystemwatcher Changed Event Is Raised Twice
Easiest Way to Read from and Write to Files
Increase Upload File Size in ASP.NET Core
Extension Method and Dynamic Object
Difference Between Const and Readonly in C#
What Is the Overhead of Creating a New Httpclient Per Call in a Webapi Client
String Was Not Recognized as a Valid Datetime " Format Dd/Mm/Yyyy"
Ef Codefirst: Should I Initialize Navigation Properties
How to Get the Index of the Current Iteration of a Foreach Loop