Cannot update record with EF, error when attaching - c#

I am receiving this error:
"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."
when attempting to update my model from the front end. I did some research and saw a post that said I should null out my navigation properties to prevent this so I did but no luck. I have also tried reaching out and grabbing all the navigation properties and fully populating them so I have the full object, this too did not work.
I am using a really nice "generic repository" which handles the state changes and everything for me. When it goes into the update method it is attaching the record and blows up when doing so.
Here is the code that is setting up the object in my WebApi controller.
public HttpResponseMessage Put(ProductModel product)
{
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
var shopId = _uow.Repository<Shop>().GetShopIdByUserId("90f36c26-9b28-464d-869f-378015ae8f0c"); //work
product.ShopId = shopId;
var newProduct = Mapper.Map<Product>(product);
newProduct.Category = null;
try
{
_uow.Repository<Product>().Update(newProduct);
_uow.Save();
}
catch (DbUpdateConcurrencyException ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
This is the Repositories implementation for update. Id prefer not to touch this as it is part of the generic repository framework I am using.
public virtual void Update(TEntity entity)
{
_dbSet.Attach(entity); // **Blows up here**
((IObjectState)entity).State = ObjectState.Modified;
}
I am using WebApi v2, EF 6.
Edit:
I had a list of test data that should not have been sent to EF, i nulled it out and it works now. Funny how you can look at something 100x but not have it click until someone tells you to do the same thing lol!

Related

Why is the DbContext closing before I attempt this LINQ query in Entity Framework using FindAsync()?

I'm on a team using an EF, Code-first approach with ODP.Net (Oracle). We need to attempt to write updates to multiple rows in a table, and store any exceptions in a collection to be bubbled up to a handler (so writing doesn't halt because one record can't be written). However, this code throws an exception saying
System.InvalidOperationException: The operation cannot be completed because the DbContext has been disposed.
I'm not sure why. The same behavior occurs if the method is changed to be a synchronous method and uses .Find().
InvModel _model;
public InvoiceRepository(InvModel model)
{
_model = model;
}
public void SetStatusesToSent(IEnumerable<Invoice> Invoices)
{
var exceptions = new List<Exception>();
foreach (var id in invoices)
{
try
{
var iDL = await _model.INVOICES.FindAsync(id);/*THROWS A DBCONTEXT EXCEPTION HERE*/
iDL.STATUS = Statuses.Sent; // get value from Statuses and assign
_model.SaveChanges(); //save changes to the model
}
catch (Exception ex)
{
exceptions.Add(ex);
continue; //not necessary but makes the intent more legible
}
}
}
Additional detail update: _model is injected by DI.
Remember that LINQ executes lazily - that is when you actually use the information.
The problem might be, that Your DbContext has gone out of scope...
Use .ToList() or .ToArray() to force execution at that time.

EF Core - can't add entity with the same related objects

I am porting app from EF 6 to EF Core 2.2. I have an object with some related objects inside, each one with database generated ID and GUID (db - postgresql).
I'm trying to create a generic method to add a whole object graph with all related objects the same way as in EF 6 - like this:
var res = context.Set<T>().Add(entity);
Before the insert, EF make temporary IDs, which will be replaced with real database IDs.
So, because inside different object I might have exactly the same objects (for better understanding, my subject area is medicine, I have several different analyzes that are performed from the same sample), in EF Core I can't add whole object graph like this - getting errors, for example:
Key (\"ID\")=(5) already exists
But in the EF 6 version, everything used to work - all objects are inserted including inner objects with correct IDs and GUIDs, without duplicates.
In both versions, temporary IDs in the same objects are also equal, but only in EF Core version, I'm getting this error.
I have tried add attributes
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
tried changing the DbContext
modelBuilder.Entity<Sample>().Property(e => e.ID).ValueGeneratedOnAdd();
but neither works for me - I think the problem is not here.
Also I found this article in Microsoft docs, which says
If the graph does contain duplicates, then it will be necessary to process the graph before sending it to EF to consolidate multiple instances into one.
but I'm not sure - is this about my case?
Am I doing this wrong or is it impossible in EF Core 2.2?
Magic sauce: Create an interface and implement it on object you don't want to save on the object graph, then simply do not set that object as modified. The paradigm that I was failing to understand was that I never really wanted to save a 'defining' object during a save when that object was being used to define the object being saved.
I save the defining objects with a separate process. Works perfectly.
public virtual T InsertOrUpdate(T oneObject)
{
T output = null;
if (oneObject.Id == Guid.Empty)
{
output = this.Insert(oneObject);
}
else
{
try
{
_dbContext.ChangeTracker.TrackGraph(oneObject, e =>
{
if (e.Entry.IsKeySet)
{
// See if the entry has interface with 'StaticObject'
List<Type> x = e.Entry.Entity.GetType().GetInterfaces().ToList();
if (x.Contains(typeof(IStaticObject)))
{
_logger.LogWarning($"Not tracking entry {e.Entry}");
}
else
{
e.Entry.State = EntityState.Modified;
}
}
else
{
e.Entry.State = EntityState.Added;
}
});
_dbContext.Entry(oneObject).State = EntityState.Modified;
_dbContext.SaveChanges();
output = oneObject;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Problem updating object {oneObject.Id}");
}
}
return output;
}
public virtual T Insert(T oneObject)
{
try
{
_dbContext.Attach(oneObject);
_dbContext.Entry(oneObject);
_dbContext.SaveChanges();
}
catch (Exception error)
{
_logger.LogError(error.Message, error);
}
return oneObject;
}

Updating existing data in EF 6 throws exception - "...entity of the same type already has the same primary key value."

I am trying to update a record using Entity Framework 6, code-first, no fluent mapping or a tool like Automapper.
The entity(Employee) has other composite properties associated with it like Addreess(collection), Department
It is also inherited from a base called User
The save method is as follows, with _dbContext being the DbConext implementation
public bool UpdateEmployee(Employee employee)
{
var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault();
if (entity == null)
{
_dbContext.Employees.Add(employee);
}
else
{
_dbContext.Entry(employee).State = EntityState.Modified; // <- Exception raised here
_dbContext.Employees.Attach(employee);
}
return _dbContext.SaveChanges() > 0;
}
I keep getting the error:
Attaching an entity of type failed because another entity of the same
type already has the same primary key value. This can happen when
using the 'Attach' method or setting the state of an entity to
'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate.
I have tried the following:
Attaching before setting to EntityState.Modified
Adding AsNoTracking() on querying if the object exists(No exception but DB is not updated) - https://stackoverflow.com/a/23228001/919426
Saving using the base entity _dbContext.Users instead of the Employee entity - https://stackoverflow.com/a/25575634/919426
None of which is working for me now.
What could I have gotten wrong for some of those solutions not to work in my situation?
EF already includes a way to map properties without resorting to Automapper, assuming you do not have navigation properties to update:
public bool UpdateEmployee(Employee employee)
{
var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault();
if (entity == null)
{
_dbContext.Employees.Add(employee);
}
else
{
_dbContext.Entry(entity).CurrentValues.SetValues(employee);
}
return _dbContext.SaveChanges() > 0;
}
This usually generates a better SQL statement since it will only update the properties that have changed.
If you still want to use the original method, you'll get rid of entity from the context, either using AsNoTracking (not sure why it didn't update in your case, it should have no effect, so the problem might be something else) or as modifying your query to prevent it from materializing the entity in the first place, using something like bool exists = dbContext.Employees.Any(c => c.Id == employee.Id) for example.
This worked for myself
var aExists = _db.Model.Find(newOrOldOne.id);
if(aExists==null)
{
_db.Model.Add(newOrOldOne);
}
else
{
_db.Entry(aExists).State = EntityState.Detached;
_db.Entry(newOrOldOne).State = EntityState.Modified;
}
I've encountered the same thing when using a repository and unit of work pattern (as documented in the mvc4 with ef5 tutorial).
The GenericRepository contains an Update(TEntity) method that attempts to Attach then set the Entry.State = Modified. The up-voted 'answer' above doesn't resolve this if you are going to stick to the uow / repo pattern.
I did attempt to use the detach process prior to the attach, but it still failed for the same reason as indicated in the initial question.
The reason for this, it turns out, is that I was checking to see if a record existed, then using automapper to generate an entity object from my dto prior to calling update().
By checking for the existance of that record, i put the entity object in scope, and wasn't able to detach it (which is also the reason the initial questioner wasn't able to detach)... Tt tracked the record and didn't allow any changes after I automapper'ed the dto into an entity and then attempted to update.
Here's the generic repo's implementation of update:
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
This is my PUT method (i'm using WebApi with Angular)
[HttpPut]
public IHttpActionResult Put(int id, Product product)
{
IHttpActionResult ret;
try
{
// remove pre-check because it locks the record
// var e = unitOfWork.ProductRepository.GetByID(id);
// if (e != null) {
var toSave = _mapper.Map<ProductEntity>(product);
unitOfWork.ProductRepository.Update(toSave);
unitOfWork.Save();
var p = _mapper.Map<Product>(toSave);
ret = Ok(p);
// }
// else
// ret = NotFound();
}
catch (DbEntityValidationException ex)
{
ret = BadRequest(ValidationErrorsToMessages(ex));
}
catch (Exception ex)
{
ret = InternalServerError(ex);
}
return ret;
}
As you can see, i've commented out my check to see if the record exists. I guess i'll see how it works if I attempt to update a record that no longer exists, as i no longer have a NotFound() return opportunity.
So to answer the initial question, i'd say don't look for entity==null before making the attempt, or come up with another methodology. maybe in my case, i could dispose of my UnitOfWork after discovery of the object and then do my update.
You need to detach to avoid duplicate primary key exception whist invoking SaveChanges
db.Entry(entity).State = EntityState.Detached;

Passing Entity Framework object to helper method for update

I'm having some trouble using a helper method to perform an update to a set of model objects. The table uses a lookup table to hold 5 records per agent/user. If I want to save the record for the agent, I need to save that record onto the AgentTransmission table, and up to 5 other records on the RelationshipCodeLookup table.
Since I have to do this five times per agent, and we must do the process in the Create and Edit methods, I created a helper method to save the records. This works fine during the create process as we're simply doing a DbContext.Add(). However when I need to perform an update, I get the error message
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
I think this has to do with the fact I'm passing the model object to my helper method, and therefore the DbContext thinking that it has two separate objects to keep track of. I say this because the lines of code that are commented out work just fine and allow me to save the object. Passing the object to the helper method, however, gets the above error.
Does anyone know of a way around this (using a helper method to perform an update)?
Controller Action
//Save relationship codes in lookup table
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode1))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode1, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode2))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode2, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode3))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode3, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode4))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode4, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode5))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode5, agenttransmission.ID);
}
Helper Method
public void SaveRelationshipCodes(RelationshipCodeLookup relCode, int id)
{
if (relCode.AgentId == 0) relCode.AgentId = id;
relCode.LastChangeDate = DateTime.Now;
relCode.LastChangeId = Security.GetUserName(User);
//Check to see if record exists and if not add it
if (db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal) != null)
{
db.Entry(relCode).State = EntityState.Detached;
}
else
{
if(relCode.RelCodeOrdinal == 0) relCode.RelCodeOrdinal = FindOrdinal(relCode);
db.RelationshipCodeLookup.Add(relCode);
}
db.SaveChanges();
}
EDIT
After scouring the web I attempted to save via this method
//Check to see if record exists and if not add it
if (db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal) != null)
{
db.Entry(relCode).CurrentValues.SetValues(relCode);
}
else
{
Member 'CurrentValues' cannot be called for the entity of type 'RelationshipCodeLookup because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet<RelationshipCodeLookup>
However.... doing that only puts me back at the start with the following error on db.RelationshipCodeLookup.Attach(relCode);
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
Try this:
db.RelationshipCodeLookup.Attach(relCode);
db.Entry(relCode).State = EntityState.Modified;
For updates you want to attach the detached object then set it's state to modified.
The issue here seems to be that the Entity Framework cannot track two objects of the same kind at the same time. Because of that I find the solution to this problem more than a little weird. By calling .Find() on the DbContext and instantiating a second copy of the model object I was finally able to save. Seems to break all the rules the EF was laying out for me in the error messages, but hey it works.
public void SaveRelationshipCodes(int id, RelationshipCodeLookup relCode)
{
if (relCode.AgentId == 0) relCode.AgentId = id;
relCode.LastChangeDate = DateTime.Now;
relCode.LastChangeId = Security.GetUserName(User);
//Check to see if record exists and if not add it
if (db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal) != null)
{
//Need to call .Find to get .CurrentValues method call to work
RelationshipCodeLookup dbRelCode = db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal);
db.Entry(dbRelCode).CurrentValues.SetValues(relCode);
}
else
{
if(relCode.RelCodeOrdinal == 0) relCode.RelCodeOrdinal = FindOrdinal(relCode);
db.RelationshipCodeLookup.Add(relCode);
}
db.SaveChanges();
}

error occurred while updating the object context

first of all here is the message
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.
the problem happens when i try to insert new data in the entityframework
My entity model
in the database i set the relation to cascade on delete and update. that is the only change i made to the relation
My Action Method :
[HttpPost]
public ActionResult CompleteRegisteration(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = new User
{
DisplayName = model.DisplayName,
FullName = model.Name,
Email = model.Email,
};
user.AuthenticationTokens.Add(new AuthenticationToken
{
ClaimedIdentifier = model.ClaimedIdentifier,
DisplayName = model.Email
});
_userRepository.InsertOrUpdate(user);
_userRepository.Save();
return RedirectToAction("Index", "Home");
}
and the user repository methods :
private readonly StoryWritingEntities context = new StoryWritingEntities();
public void InsertOrUpdate(User user)
{
context.Users.Attach(user);
context.ObjectStateManager.ChangeObjectState(user,
user.Id == default(int)
? EntityState.Added // if true then this is a new entry
: EntityState.Modified); // if false this is an Existing entry
}
public void Save()
{
context.SaveChanges();
}
the problem is caused by context.SaveChanges() there is a record inserted in the users table but nothing is inserted in the AuthenticationTokens table
If you simply did the following this wouldn't happen:
context.Users.AddObject(user);
content.SaveChanges();
I suspect the problem is occurring because EF doesn't know about the AuthenticationToken object, it's not being attached to the context because it's added to a disconnected entity which is then attached to the context.
You either need to let EF handle the whole object graph connectivity situation or you need to do it all yourself. Mixing and matching like this doesn't work.
Try something different, like:
if(model.Id != null)
{
UpdateModel(user);
}
else
{
_userRepository.Insert(model)
}
_userRepository.Save();
And the _userRepository.Insert would be:
public void Insert(User user)
{
context.Users.AddObject(user);
}
I got this error because I was trying to edit a record/row in the table, but the code was adding a row with the same ID.
So I just changed the
ctx.table.Add(entity object);
too
ctx.Entry(entity object).State = EntityState.Modified;
in the database i set the relation to
cascade on delete and update
1) I believe that if you setup cascading delete directly in the database you also need to define it in your model. The settings in the model designer are in the properties window of the relevant association (click on the association line between the two entities in the designer surface, then select "Properties").
2) I also believe that cascade on update is not supported in EF. Try to remove cascade on update in the database and check if it works then.

Categories

Resources