I am working on .net core entity framework. I have two list of class type. One for update and other for new entry, adding new records all worked fine but which is achieved by context.[Model].Add but update which is done by context.[Model].Update throw exception update i know no record been updated as it is running on local.
$exception {Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: 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.
Code
List<AnswerDataModel> surveyResponseListToCreate = new
List<AnswerDataModel>();
List<AnswerDataModel> surveyResponseListToUpdate = new
List<AnswerDataModel>();
if (surveyResponseListToUpdate.Count > 0)
{
foreach (var answerObject in surveyResponseListToUpdate)
{
Context.Answers.Update(answerObject);
if (answerObject.AnswerOptions.Count > 0)
{
foreach (var optItem in answerObject.AnswerOptions)
{
AnswerOptionDataModel answOpt = new AnswerOptionDataModel();
answOpt = optItem;
Context.AnswerOptions.Update(answOpt);
}
}
}
}
var recordsAffected = Context.SaveChanges();
if (!UsingExternalTransaction)
{
FinalizeTransaction(recordsAffected);
}
I can't resist a quote:
"I do not think [your code] means what you think it means."
Assuming that surveyResponseListToUpdate was a list of entities previously loaded and modified:
if (answerObject.AnswerOptions.Count > 0) // Unnecessary...
{
foreach (var optItem in answerObject.AnswerOptions)
{
AnswerOptionDataModel answOpt = new AnswerOptionDataModel(); // does nothing.
answOpt = optItem; // references existing answer option..
Context.AnswerOptions.Update(answOpt);
}
}
The whole block boils down to:
foreach (var optItem in answerObject.AnswerOptions)
Context.AnswerOptions.Update(optItem);
The error you are likely running into is because Update will recurse through navigation properties automatically, so when the parent (Answer) is updated, it's AnswerOptions will be updated as well. So when you go through the extra steps to try and save answer options, they've already been updated when the answer was saved. Provided the Answer was loaded by the same context that you are saving it to, you should be in the clear with:
foreach (var answerObject in surveyResponseListToUpdate)
Context.Answers.Update(answerObject);
var recordsAffected = Context.SaveChanges();
This should update the answer and it's associated answer objects. Even if options were added or removed, the change tracking should do it's job and ensure all of the associated data records are updated.
The extra if checks and such aren't necessary and just add to nesting depth making code harder to read.
However, I suspect that your real code is doing something different to the example given that my tests where I tried to reproduce your error, the code worked fine even updating the child references after updating the parent. If the above still raises issues, please update your example with the code you are running.
Related
I am using EF 6 Code First and I need to delete an item and then also update a different item within a collection of Entities. If I try to delete one item and then modify a completely different item I get the error message "An object with the same key already exists in the ObjectStateManage" This is inaccurate because there are two objects with completely different PK IDs but when the update happens it throws the error. If I comment out the code to delete then the update works just fine with multiple items to update. Why would it complain about the "same key" when the keys are different?
foreach (var phone in phones)
{
if (!_isValidPhone(phone))
{
if(phone.PhoneId != 0)
{
var deletePhone = _db.Phones.FirstOrDefault(r => r.PhoneId == phone.PhoneId);
_db.Entry(deletePhone).State = EntityState.Deleted;
continue;
}
}
if (_isNewPhone(phone))
{
AddNewPhone(phone, _person);
}
else
{
UpdatePhoneData(phone, _person.Phones.FirstOrDefault(r => r.Order == phone.Order));
}
}
private void UpdatePhoneData(Phone phoneFrom, Phone phoneTo)
{
phoneTo.Note = phoneFrom.Note;
phoneTo.PhoneNumber = phoneFrom.PhoneNumber;
phoneTo.Order = phoneFrom.Order;
_db.Entry(phoneTo).State = EntityState.Modified;
}
If a phone is not valid and has id you try to add it to the context in two ways:
While deleting it:
_db.Entry(deletePhone).State = EntityState.Deleted;
Besides, when checking if it's new or not you either add or update it. Thats the problem.
So, what you need to do is wrap the add or update part inside an else, to add or update only if the phone has not been deleted.
This is not really a logic issue, the phone that was being updated is completely different than the phone that was being deleted. The issue is in the object statemanager. Because I was doing this in the delete
var deletePhone = _db.Phones.FirstOrDefault();
And then later I had a separate list of Phones where I try to set one of them to modified
_db.Entry(phoneTo).State = EntityState.Modified;
Well the object state manager now has each phone loaded twice basically. So if I just use the _person.Phones for both my delete and modify, then the Phones list is only loaded once and there are now duplicate keys.
_db.Entry(_person.Phones.FirstOrDefault(r => r.PhoneId == phone.PhoneId)).State = EntityState.Deleted;
I am trying to save hundreds of thousands of records using Entity framework. After saving few hundreds of thousands of records I get following error:
:System.OutOfMemoryException
My code
foreach (BibContent objbibcontents in lstBibContent)
{
db.BibContents.AddObject(objbibcontents);
c = c + 1;
if (c == 1000)
{
db.SaveChanges();
c = 0;
}
}
I noticed after saving 1000 records my db is not overriding another 1000 records. it is adding them into my dbcontext.
I am creating a new instance after 1000 records but my db still has the previous object's data. See my code
foreach (var objbibcontents in lstBibContent)
{
vibrantEntities db1 = new vibrantEntities(szConStr);
lstBibCon.Add(objbibcontents);
// db.BibContents.AddObject(objbibcontents);
c = c + 1;
if (c == 1000)
{
foreach (BibContent bibobject in lstBibCon)
{
db1.BibContents.AddObject(bibobject);
}
lstBibCon.Clear();
db1.SaveChanges();
c = 0;
flag = 1;
}
}
How many objects are you going to save and how big is single object? DbContext holds references to all objects you added with AddObject call. Calling SaveChanges does not purge its internal data structures so if you call your code for 1M objects you will have 1M object in memory and they will be fully alive because their root object for GC will be the context instance which is still in scope of your running code.
If you want to avoid the memory issue you should use a new context instance for every 1000 records (or even every record). The only difference between running SaveChanges for 1000 records and for single record is the automatically involved transaction.
I search on Web and Finally i found good solution for my problem.
Fastest Way of Inserting in Entity Framework
I have employee details of more than 4000 employees. While retrieving those rows, I am faced with performance issues due to the looping. So what can I do to improve performance?
This is the looping I mentioned:
List<EmployeesEntityObject> lstEmployee = new List<EmployeesEntityObject>();
foreach (var item in lst)
{
EmployeesEntityObject obj = new EmployeesEntityObject();
obj.EmployeeID = item.EmployeeID;
obj.EmployeeName = item.EmployeeName;
lstEmployee.Add(obj);
}
You could try and see what happens when you completely "linqify" your code:
var lstEmployee = lst.Select(emp => new EmployeesEntityObject
{
EmployeeID = item.EmployeeID,
EmployeeName = item.EmployeeName
}).ToList();
But as marc_s says, there is no apparent cause for any performance issues in your code. Unless, as said, the constructor and/or the setters of the properties conceal time-consuming code. Both of which, by the way, would not be recommendable.
The problem I am having is as follows, due to caching issues we are putting the following before the start of every ET query:
DataBase.Refresh(System.Data.Objects.RefreshMode.ClientWins, DataBase.PublicUsers);
However, this is causing pages to take ages to load as the above command makes two calls to the database.
Does anybody know away to stop EF from caching without having to put that command before every query?
To answer your initial question. If you don't want context to cache data you must execute query without change tracking.
Database.Hubs.MergeOption = MergeOption.NoTracking;
return DataBase.Hubs
.Where(h =>
h.BusinessId == null
&& h.TypeId != (int)HubType.BusinessPlace
&& h.ParentHubId != null
);
But this will not solve your architecture issue related to static / shared context in a web app. You must change your architecture if you really want to create working application.
You can set the MergeOption to all EntitieSet after create the context. Some like this:
var objSetProps = ctx.GetType().GetProperties().Where(prop => prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(ObjectSet<>));
foreach (PropertyInfo objSetProp in objSetProps)
{
ObjectQuery objSet = (ObjectQuery)objSetProp.GetValue(ctx, BindingFlags.GetProperty, null, null, null);
objSet.MergeOption = MergeOption.PreserveChanges;
}
Read about the MergeOption here: http://msdn.microsoft.com/en-us/library/system.data.objects.mergeoption.aspx
Your will use NoTracking, I think.
But, iy you whant CLEAR the "cached" entities, detaching it.
var entidades = Ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged);
foreach (var objectStateEntry in entidades)
Ctx.Detach(objectStateEntry.Entity);
Where Ctx are my Context.
I'm trying to test detaching an entity from one context, making modifications to it, creating a new context, attaching it, and having the changes made between sessions persist. I don't seem to be able to get this working appropriately. I've tried calling DetectChanges as well as ApplyCurrentValues w/ no success. Below is what I've got so far. These aren't POCO's and I don't want to treat them as such. I just want to be able to detach an entity, make changes to it, and re-attach it. Thanks!
OCConsumer consumer;
using (var ctx1 = new CMSStagingContext())
{
consumer = (from c in ctx1.OCConsumers
select c).FirstOrDefault();
Console.WriteLine("Retrieved {0} - {1} {2}",
consumer.CustomerId, consumer.FirstName, consumer.LastName);
ctx1.Detach(consumer);
}
consumer.BirthDate = "10/22/1981";
using (var ctx2 = new CMSStagingContext())
{
ctx2.Attach(consumer);
ctx2.ApplyCurrentValues("OCConsumers", consumer);
ctx2.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave | System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
}
When you attach an object to a context, the context is going to presume that the object is unmodified, unless you tell it otherwise. The simplest way to do this is to attach the object to the context first, then modify it. So you could change your code to:
OCConsumer consumer;
using (var ctx1 = new CMSStagingContext())
{
consumer = (from c in ctx1.OCConsumers
select c).FirstOrDefault();
Console.WriteLine("Retrieved {0} - {1} {2}",
consumer.CustomerId, consumer.FirstName, consumer.LastName);
ctx1.Detach(consumer);
}
using (var ctx2 = new CMSStagingContext())
{
ctx2.Attach(consumer);
consumer.BirthDate = "10/22/1981";
ctx2.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave | System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
}
Another approach would be to use Context.ObjectStateManager.ChangeObjectState.