I'm trying to update an existing entity.
I have the following code:
public MamConfiguration_V1 Save(MamConfiguration_V1 item)
{
mMaMDBEntities.MamConfiguration_V1.Attach(item);
mMaMDBEntities.ObjectStateManager.ChangeObjectState(item, System.Data.EntityState.Modified);
mMaMDBEntities.SaveChanges();
return item;
}
But the Attach methods throws an exception:
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.
How can I fix this?
Seems like you have some relationship with foreign key field and a navigation property in the item, and those fields have conflicting values. This occurs when you load an entity and its related entities, change the relationship at one end, mark only that end as Modified and attempt to save. Make sure you modify relationship at both ends and mark all the affected entities as Modified before calling SaveChanges.
I encountered this exception under a different set of circumstances, and am posting here since this question comes up when the error message is searched.
The exception was thrown when calling IObjectContextAdapter.ObjectContext.AttachTo(entitySetName, entity) with a partially-loaded entity. The foreign keys on the entity were defined, but the navigational properties were not loaded. (That is, O.ItemID had a value, but O.Item was null). The specific circumstances did not allow O.Item to be loaded.
The problem turned out to be that the Object State Manager had loaded the object in a separate method and was already tracking the object defined with the same keys. Since the separate method did not need to track the object state, the issue was resolved by calling IQueryable.AsNoTracking() within that method.
What is the definition of the item object? It seems that in some of its collections that set the realionship with other entities exist some type of conflict. You could try to clear all the collections to see if the problem persists, but in this case you lost the foreign key assignment. But perhaps it could help you to locate the problem.
This could be a tip. When I try to attach an existing entity to the context, I use to do the following:
mMaMDBEntities.Entry<MamConfiguration>(item).State = System.Data.EntityState.Modified;
You can add the using of System.Data to avoid the needed to write it all the time.
This attach the entity in the state that you want, modified in this case and track the changes. This is one line instead of two.
The issue for me was that entity framework had loaded my object in multiple places, so when I updated a foreign key, there were now two references to the same object, one with a foreign key pointing to record a and one with a foreign key pointing to record b, which caused an error since my relationship is one to one. To resolve it, I used context.Entry(Object).State = EntityState.Detached, reloaded the object, made the foreign key change and then saved my changes
Lets say you have the following schema:
If you want to edit the CurrentLocationId in Person, you also need to edit the CurrentLocation object embedded in the Person object. EF will automatically populate the CurrentLocation object because CurrentLocationId has a foreign key in the CurrentLocation's table. When you edit the CurrentLocationId without updating the CurrentLocation object as well, they become out of sync. This is what causes the exception in this case.
So let's say you needed to update the Person object's CurrentLocationId. We'll assume you pre-fetched the Person data and the Location data.
public class DbData
{
List<Person> PersonList;
List<Location> LocationList;
public DbData()
{
using (var context = new MyContext())
{
PersonList = context.Persons.ToList();
LocationList = context.Locations.ToList();
}
}
public void UpdatePersonLocation(Person person, int newLocationId)
{
using (var context = new MyContext())
{
var location = LocationList.Where(l=>l.id==newLocationId).Single();
//you need to update both the id and the location for this to not throw the exception
person.CurrentLocationId == newLocationId;
person.CurrentLocation == location;
context.Entry(person).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
//or if you're giving it the location object...
public void UpdatePersonLocation(Person person, Location location)
{
using (var context = new MyContext())
{
//you need to update both the id and the location for this to not throw the exception
person.CurrentLocationId == location.id;
person.CurrentLocation == location;
context.Entry(person).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
}
This might be an old post but the following worked for me
set the SaveOptions option to SaveOptions.DetectChangesBeforeSave
Related
I´m getting an error when using RemoveRange to bulk delete data, in my unit tests, using InMemoryDatabase.
Here is the code:
public void DeletePatient(Paciente patient)
{
var schedules = dbContext.Schedules.AsNoTracking().Where(x => x.PatientId == patient.Id).ToList();
dbContext.Schedules.RemoveRange(schedules);
dbContext.Patients.Remove(patient);
}
This throws this error:
InvalidOperationException: The instance of entity type 'Schedule' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
But, if I perform a foreach and reload each entity, it works:
foreach(var item in schedules)
{
var h = dbContext.Schedules.Find(item.Id);
dbContext.Remove(h);
}
The same foreach, using the item directly gives same error:
foreach(var item in schedules)
{
dbContext.Remove(item);
}
Try removing the AsNoTracking clause. I haven't tested it but my guess is this is causing EF to re-read the entities from the database and not finding the ones already in the context. Without the clause it should find the actual entities in the context to be removed.
I'm using Entity Framework Core together with the repository pattern. To help me out, I coded one base repository with the basic CRUD methods. The update method is as follows:
public void Update(TEntity entity)
{
var contextEntry = _context.Entry<TEntity>(entity);
if (contextEntry.State == EntityState.Dettached)
{
_context.Attach(entity);
}
contextEntry.State = EntityState.Modified;
_context.SaveChanges();
}
Given the BaseRepository class containing this method, I created one User repository inheriting from this
public class UserRepository : BaseRepository<User>, IUserRepository
{
}
And I've used this in the PUT method of one Web API coded with ASP.NET Core
[HttpPut("~/api/users/{id}")]
public IActionResult Put(int id, [FromBody] User user)
{
if (user == null || user.UserId != id)
{
return BadRequest();
}
userRepository.Update(user);
return new NoContentResult();
}
Now, when issuing a request, I get one error in the _context.Attach(entity) line. The exception says that it can't add the entity for tracking because there is already another entity with the same key being tracked.
When debugging I saw that contextEntry.State was set to Unchanged. Hence, it is obviously not equal to EntityState.Dettached. Still, the execution got inside the if statement and tried to attach the entity.
Something is quite wrong here. Is this a bug? Or am I doing something very wrong? I believe that I'm the one doing something very wrong with this update strategy, but I'm unsure about it. In that case, what is wrong with my approach?
EDIT: I updated the Update method to use just _context.Update(entity) and after _context.SaveChanges(). Still, the _context.Update(entity) throws one InvalidOperationException with this message:
Additional information: The instance of entity type 'User' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
You are getting same entity from database some where in the project that's why it give you error.
In Update method you just add the entity in the context that's why you get contextEntry.State Unchanged.
You can fix this problem in two ways.
You need to call the Detach method on same entity when you get it from database.
copy the values from entity which you received in Update method to the existing context entity and save that entity in database.
All the information is in the exception message... you already have another copy of the entity with that primary key attached.
I would recommend one of the following (preferred first):
Use a new Context for each action, don't have a long-lived repository/context
Use .Set<TEntity>.Find(object[] key) on your context using the Primary Key, in order to retrieve any entity you already have.
In your current update method, use the Set<TEntity>.Local.Find(..) to check if it already exists
I've searched... I promise. The closest I came to an answer is a post where EF self tracking entities are used and I don't understand how to make it apply to my situation.
In my case, I have an existing record where I want to change a value from an int to a NULL.
Table Pseudocode:
PICKLIST_VALUE
Picklist ID (int, PK, Identity, NOT NULL)
PicklistValue (String, NOT NULL)
PERSON
Person_ID (int, PK,Identity, NOT NULL)
Person_Name (varchar(100), NOT NULL)
Person_Prefix (int, FK, NULL)
Person_Suffix (int, FK, NULL)
FOREIGN KEY (Person_Prefix) REFERENCES PICKLIST_VALUES.Picklist_ID
FOREIGN KEY (Person_Suffix) REFERENCES PICKLIST_VALUES.Picklist_ID
I am using EF6, DB first... With a DAL based on Magnus Montin's post at https://blog.magnusmontin.net/2013/05/30/generic-dal-using-entity-framework/
In my code I have POCO definitions for each entity (table). A Person can have a prefix (Mr., Mrs., etc.) and a Person can have a suffix (Jr., Sr., etc.) If I accidentally set a suffix and then realize that it's incorrect, I want to be able to remove the suffix:
someperson.Person_Suffix = null; // Set FK property to null
someperson.PICKLIST_VALUE = null; // Set navigation property to null
someperson.EntityState = EntityStates.Modified;
DAL.UpdatePerson(someperson);
The UpdatePerson method resolves to this bit based on the DAL article:
public virtual void Update(params T[] items)
{
using (var context = new Entities())
{
DbSet<T> dbSet = context.Set<T>();
foreach (T item in items)
{
dbSet.Add(item);
foreach (DbEntityEntry<IEntity> entry in context.ChangeTracker.Entries<IEntity>())
{
IEntity entity = entry.Entity;
entry.State = GetEntityState(entity.EntityState);
}
}
context.SaveChanges();
}
}
protected static System.Data.Entity.EntityState GetEntityState(CB.DomainModel.EntityState entityState)
{
switch (entityState)
{
case DomainModel.EntityState.Unchanged:
return System.Data.Entity.EntityState.Unchanged;
case DomainModel.EntityState.Added:
return System.Data.Entity.EntityState.Added;
case DomainModel.EntityState.Modified:
return System.Data.Entity.EntityState.Modified;
case DomainModel.EntityState.Deleted:
return System.Data.Entity.EntityState.Deleted;
default:
return System.Data.Entity.EntityState.Detached;
}
}
I don't know enough of what's going on under the covers of the EF stuff and what the .edmx and T4 template code does. If I did, I could probably figure this out. But if I change the values from one int to another, the changes are persisted to the database after this call. But if I null the values, as in this example, to "clear" the foreign key reference, the original value comes back.
It must have something to do with the way the object registers with the ChangeTracker once it's added to the dbSet. Prior to that, it's just a plain old object.
So my question is why is the ChangeTracker (or whatever bit of auto-generated code) allowing the FK to change from one int value to another, but it won't persist the null? And how do I "fix" it or work around the issue?
Thanks.
J
UPDATE:
The key is definitely nullable. Both in the DB and in the class definition. The link to the other post seems to be a different scenario... They want to automatically null the foreign key when the foreign entity is deleted. (I will take a closer look, just to be sure there isn't an answer there, but it seems to be a different issue.) Just for fun I created a new object and copied the properties over, with the exception of the nav properties and set the FK property I want to remove to null and that did work. The null FK was persisted, and both the nav prop and the foreign key prop were null. So it has something to do with the change tracker and how it is maintaining a reference to the entity object, despite all the POCO-ness of it. So, at the very least, I have a work-around. But I would like to understand what's actually happening and how to address it.
SOLUTION:
I just discovered, this morning, that a change I made to the T4 template must have been over-ridden, at some point, and I lost a line setting ProxyCreationEnabled to false for the DBContext... Once I realized that and put the fix in, I was able to persist the changes properly. So, in effect, I was fighting something in the change tracker since it was creating the dynamic proxies for the entities retrieved from the DB. (I think.)
I just discovered, this morning, that a change I made to the T4 template must have been over-ridden, at some point, and I lost a line setting ProxyCreationEnabled to false for the DBContext... Once I realized that and put the fix in, I was able to persist the changes properly. So, in effect, I was fighting something in the change tracker since it was creating the dynamic proxies for the entities retrieved from the DB. (I think.)
I'm trying to copy an entity like described here. In my entity-wrapper base-class I have the following code to copy/clone an entity.
public TBaseEntityModel Clone(TPrimaryKey newPrimaryKey)
{
var newEntity = Activator.CreateInstance<TEntity>();
var clone = DbContext.Entry(newEntity);
clone.State = EntityState.Added;
DbContext.Entry(newEntity).CurrentValues.SetValues(TheEntity);
clone.State = EntityState.Detached;
var cloneEntityModel= (TBaseEntityModel)Activator.CreateInstance(typeof(TBaseEntityModel), DbContext, newEntity);
cloneEntityModel.PrimaryKeyValue = newPrimaryKey;
return cloneEntityModel;
}
After I call the Clone-method on my concrete entity, it has also it's new Primary Key set to the given value of newPrimaryKey.
The propblem occurs when I call SaveChanges() on the underlying context.
It then throws:
Violation of PRIMARY KEY constraint '...'. Cannot insert duplicate key in object 'dbo....'. The duplicate key value is (553a7aa9-0ac2-40a0-820f-43a3b4af745f).
But when I look at my clone, the PK is set to another value.
So I guess it is something inside the ObjectContext or even deeper inside.
But I have no idea how to get away the error.
I suspect that the database is trying to generate the primary key, but you are also trying to specify it explicitly before saving. If you want to specify values for identity columns then you need to jump through some hoops (How can I force entity framework to insert identity columns?). Otherwise, just don't set the value and it will be generated for you when you perform the insert.
I am trying to log changes to the database so that the user can see who changed what. I am using the DbEntityEntry to go through and log the DbPropertyEntity that have been modified. I am running into a problem when I want to log changes to a navigation property. I use the Reference() method to get reference to the navigation property, however unlike DbPropertyEntity, DbReferenceEntry does not have a OriginalValue only a CurrentValue attribute. How do you get the OriginalValue of a navigation property?
//Get the field that hold the id of the foreign key
var field = entry.Property(x => x.field);
//Check to see if the user changed the value
if (field.IsModified)
{
//Get the reference property associated with the field
var fieldRef = entry.Reference(x => x.fieldRef);
//Log the id change
Log(field.Name, field.CurrentValue, field.OriginalValue);
//Can't get the OriginalValue
Log(fieldRef.Name, fieldRef.CurrentValue, ???);
}
What exactly do you expect to log for the reference?
If you want to log changes in relation (= changes in foreign key) you should map the foreign key as separate property and it will be logged as normal field. If you don't map the foreign key you have an independent association. Support for independent associations in DbContext API is limited. The full support is only in ObjectContext API (you can use it from DbContext API) but it will still not solve your problem - independent association cannot be in modified state. It can be either in unchanged, added or deleted state. If you change the reference the old association is marked as deleted and new is added = there are two separate association objects tracked by the context.