Bulk insert data entity framework 7 - c#

Normally In my previous project, I can do bulk insert by passing a list of object as code below
public void Create(List<ApplicationUserRole> item)
{
foreach (var data in item)
{
_dbContext.ApplicationUserRole.Add(data);
}
_dbContext.SaveChanges();
}
But for now i keep hitting error
InvalidOperationException: The instance of entity type
'Docdoc.Models.ApplicationUserRole' cannot be tracked because another
instance of this type with the same key is already being tracked. For
new entities consider using an IIdentityGenerator to generate unique
key values.
I need change my code at below to work
foreach (var data in item)
{
_dbContext.ApplicationUserRole.Add(data);
_dbContext.SaveChanges();
}
I know It is very bad practice. The performance will be very slow by insert large amount of data
Any solution for this problem?

The exception message you saw may not be fixed by calling "SaveChanges" after each Add. The root cause of your problem is that your instance of DbContext already has a ApplicationUserRole entity with the same key (guessing it is ApplicationUserRole.Id or something). This error is common and is often caused by manually setting temporary key values, e.g. setting ApplicationUserRole.Id to -1. (See https://github.com/aspnet/EntityFramework/issues/4488 for example.)
If the error is not being caused by incorrectly setting temp key values, then also make sure your instance of DbContext is short-lived and only used in one thread. In other words, use DbContext for one operation only.
public void Create(List<ApplicationUserRole> item)
{
using (var context = new MyContext())
{
context.ApplicationUserRole.AddRange(data);
context.SaveChanges();
}
}

Related

Removing range using EF Core

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.

Is this an EF Core bug or an I doing it wrong to update one entity?

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

A referential integrity constraint violation occurred

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

ASP.NET C# Entity Framework - How to update Foreign Key properly?

I'M quite new to this EF but I think I'M making progress. Anyway, it appears I do NOT know how to Update an object that is RELATED by a Foreign Key.
My DbRelation is:
And I'm trying to UPDATE the one members LANGUAGEID and here is the Context I invoke:
public class ManagerBase
{
private static NoxonEntities _entities = null;
public NoxonEntities Entities
{
get
{
if (_entities == null)
_entities = new NoxonEntities();
return _entities;
}
}
}
There are many things I have tried. Here is one:
1)
MemberManager currentMemberManager = new MemberManager();
var Mem = currentMemberManager.MyEntities.Member.SingleOrDefault(c => c.Id == 2);
var Lang = currentLanguageManager.Entities.Language.SingleOrDefault(c => c.Id == 1);
Mem.Language = Lang;
//Or
Mem.LanguageId = Lang.Id;
currentMemberManager.Save(Mem);
In appreach 1, I get an error like
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.
2)
//Managers uses ManagerBase class as a Base class
MemberManager currentMemberManager = new MemberManager();
currentMemberManager.Save(Globals.CurrentMember.Id, Globals.CurrentLanguage.Id);
//These Global objects coming from a Http Session
//You may also say not to keep whole member object in session which I'll not after I figure this out
Here is my SAVE method and where I have the actual problem:
public void Save(Member entity)
{
var Data = base.Entities.Member.First(c => c.Id == entity.Id);
if (Data != null)
{
Data = entity;
base.Entities.SaveChanges();
}
}
}
In appreach 1, I get an error in this code in EF model edmx file
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int32 LanguageId
{
get
{
return _LanguageId;
}
set
{
OnLanguageIdChanging(value);
ReportPropertyChanging("LanguageId");
_LanguageId = StructuralObject.SetValidValue(value);
ReportPropertyChanged("LanguageId");
OnLanguageIdChanged();
}
}
And the error is
Object reference not set to an instance of an object.
Clearly I'm in the wrong way with EF.
Could you please help me and show how to properly Update a relation Id (ForeignKeyId) ?
Thank you so much.
Yes, you are using EF in many wrong ways. First, as Arran points out, you should never make your data context static. I know that seems easier, but it's a huge problem because data contexts are designed to be created and destroyed frequently.
If you don't, then the object graph continues to grow until the app pool is eventually exhausted, plus you can run into all kinds of problems with concurrent access. Static objects are shared between all threads, so imagine if two users are using your app at the same time, they will both be using the same data context and would stomp all over each other.
Second, you're using a singleton, which is one of the most reviled patterns there are, for a lot of reasons. They have their uses, but they're far more rare than most people use them.
Third, based on the error messages, it sounds like your data model may not be in sync with your EF model, and thus it's getting confused and throwing exceptions. Have you made changes to your database structure without updating your ef model? Bear in mind that regenerating doesn't always update everything, and sometimes you have to either update it manually, or delete your objects and re-add them.
Fourth, your real problem (trust me, the ones I've laid out are also real problems, that WILL bite you in the rear at some point) lies in this code:
var Data = base.Entities.Member.First(c => c.Id == entity.Id);
if (Data != null)
{
Data = entity;
base.Entities.SaveChanges();
}
The first thing you have to realize is that EF returns tracked objects, these objects have references in EF and track the changes that occur. What you are doing here is getting an object, and then throwing it away and replacing it with a non-tracked object that was passed in.
You have to update the object returned from the First method, and not replace it with a new one.

Do Entity Framework Relationship Properties Update?

I have some code like this in a WCF web method,
List<LocationInRoad> locationInRoad = new List<LocationInRoad>();
foreach (CarWorkLocationLink locationLink in source.CarWorkLocationLinks)
{
locationInRoad.Add(LocationInRoadMapper.MapTo(locationLink.CarWorkLocationType.WorkLocationTypeID));
}
destination.LocationInRoad = locationInRoad.ToArray();
Sometimes (perhaps once a week) in production an error occurs,
InvalidOperationException has occured Message: Collection was modified; enumeration operation may not execute.
So it seems to be telling me that the 'source.CarWorkLocationLinks' collection has been modified part way through enumerating the list in the foreach loop.
So to explain, 'source' is an entity framework entity loaded from our database and 'CarWorkLocationLinks' is defined on that entity like this,
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("CarManagerModel", "FK_CarWorkLocationLink_CarDetail", "CarWorkLocationLink")]
public EntityCollection<CarWorkLocationLink> CarWorkLocationLinks
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<CarWorkLocationLink>("CarManagerModel.FK_CarWorkLocationLink_CarDetail", "CarWorkLocationLink");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<CarWorkLocationLink>("CarManagerModel.FK_CarWorkLocationLink_CarDetail", "CarWorkLocationLink", value);
}
}
}
I.e. it is an entity relationship to another table. So I guess the question is can an 'EntityCollection' be modified after it is loaded if something in the database changes?
So basically overall the code above fits into the WCF call like this,
public APIEntity WCFCall(parameters)
{
using (EntityContext context = new EntityContext())
{
// loading entity (database entity that is)
// creating API entity (this is a POCO object to control what is exposed over the WCF service)
// running the loop as shown above on the 'loaded entity' and popluating fields in the poco object
// returning the poco object
}
}
So I am not sure why the error I mentioned should occur. Is is self contained.
If you are using shared context (you are not creating ObjectContex per request / unit of work) then the answer is yes and the explanation is here - ObjectContext creates for each record identified by primary key only one entity instance and this instance is reused for each subsequent requests (it is called Identity map pattern). So if you share the context and you have multithreaded application (like web service, asp.net, etc.) all threads can concurrently use the same entity instance and modify same collection of related objects.

Categories