How to get rid off "An entity object cannot be referenced by multiple instances of IEntityChangeTracker"? - entity-framework

I have a model in Ado.Net EF.
I have a one to many relation and when I want to Add the entities I get the error
"An entity object cannot be referenceed by multiple instances of IEntityChangeTracker"
Any clue?
Something similar to
Template template = new Template();
...
...
while (from < to)
{
Course course = new Course();
.....
template.Course.Add(course);
.....
}
courseEntities.AddToTemplate(template); // Problem line
courseEntities.SaveChanges();

I was getting this message until i started to store the data context in the HttpContext.Items Property. This means you can use the same data context for the current web request. That way you don't end up with 2 data contexts referencing the same entities.
Here is a good post on DataContext Life Management.
I hope it helps.
Dave

"template", or something that it references, has already been added to courseEntities or another context. I don't see anything in the code you show it would do that, but it is certainly happening. Perhaps it's happening in some of the code that you've trimmed. Look at the EntityState property of "template" in the debugger, and look at the EntityState of the properties of "template" as well. This should help you find out which entity instance is already in a context.

I already realize the problem. I have another relation and I get the other entity from another context.

Let me relate my experience with this nasty error and point out the terrain chasing it will take you over leading to a tremendously simple solution.
CompanyGroup is pretty simple. It has a name and it has a Company object.
I started with this:
1 public static void Add(CompanyGroup item)
2 {
3 try
4 {
5 using (Entities scope = new Entities())
6 {
7 scope.AddToCompanyGroup(item);
8 scope.SaveChanges();
9 }
10 }
11 catch (Exception ex)
12 {
13 LogException(ex, item);
14 throw;
15 }
16 }
And got this error:
{"An entity object cannot be
referenced by multiple instances of
IEntityChangeTracker."}
So, I added this between lines 6 and 7:
(IEntityWithChangeTracker)item).SetChangeTracker(null);
That rewarded me with:
{"The object cannot be added to the
ObjectStateManager because it already
has an EntityKey. Use
ObjectContext.Attach to attach an
object that has an existing key."}
So I changed
scope.AddToCompanyGroup(item);
to
scope.Attach(item);
Now it complained about:
{"An object with a temporary EntityKey
value cannot be attached to an object
context."}
(beginning to sound like some of the girls I dated in my youth -- never content -- but I digress)
So I made the entity key null (didn't work) and used the method to create new (didn't work, either)
Along the way, I got this error, too:
{"The source query for this
EntityCollection or EntityReference
cannot be returned when the related
object is in either an added state or
a detached state and was not
originally retrieved using the
NoTracking merge option."}
The Solution?
Replace the core, lines 7 and 8, with:
CompanyGroup newcg = new CompanyGroup();
newcg.GroupName = item.GroupName;
newcg.Company = scope.Company.Where(c => c.CompanyID == item.Company.CompanyID).First();
scope.AddToCompanyGroup(newcg);
scope.SaveChanges();
Essentially, I took the data passed via 'item', and moved it to newly created object of the same type that introduces the same
scope as the one used in the Add.

I hope this is the simplest and correct solution. You need one db context per httprequest.
EF4 Code First template Global.asax.cs
http://gist.github.com/574505
void MvcApplication_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Items[SessionKey] = new Db();
}
void MvcApplication_EndRequest(object sender, EventArgs e)
{
var disposable = HttpContext.Current.Items[SessionKey] as IDisposable;
if (disposable != null)
disposable.Dispose();
}

Please initialize your Entities only one time.
Like as
If You more than one time initialize your Entities.
You will get the error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
ex:
public class Test
{
private Entities db=new Entities();
public static void Add(CompanyGroup item)
{
try
{
db.CompanyGroup.Add(item);
db.SaveChanges();
}
catch (Exception ex)
{
}
}
}

This problem I was solved by removing from the object that I update, extra relationships with other entities (Virtual). Left only their ID.
This is wrong code
dataFileEntity.IterParameterValue = parameterValueEntity.ParameterValue;
dataFileEntity.IterParameterValueId = parameterValueEntity.ParameterValue.Id;
dataFileEntity.ResultParameter = parameterValueEntity.ResultParameter;
dataFileEntity.ResultParameterId = parameterValueEntity.ResultParameter.Id;
dataFileEntity.RawDataResult = result.Value;
This is right
dataFileEntity.IterParameterValueId = parameterValueEntity.ParameterValue.Id;
dataFileEntity.ResultParameterId = parameterValueEntity.ResultParameter.Id;
dataFileEntity.RawDataResult = result.Value;
RequestTestRawDataFileRepository.AddOrUpdate(dataFileEntity);
Я эту проблему решила, убрав из объекта, который я апдейтила лишние связи с другими сущностями (Virtual). Оставила только их id.

Related

EF Core 2.0: How to discover the exact object, in object graph, causing error in a insert operation?

I have a complex and big object graph that I want to insert in database by using a DbContext and SaveChanges method.
This object is a result of parsing a text file with 40k lines (around 3MB of data). Some collections inside this object have thousands of items.
I am able to parse the file correctly and add it to the context so that it can start tracking the object. But when I try to SaveChanges, it says:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: String or binary data would be truncated.
I would like to know if there is a smart and efficient way of discovering which object is causing the issue. It seems that a varchar field is too little to store the data. But it's a lot of tables and fields to check manually.
I would like to get a more specific error somehow. I already configured an ILoggerProvider and added the EnableSensitiveDataLogging option in my dbContext to be able to see which sql queries are being generated. I even added MiniProfiler to be able to see the parameter values, because they are not present in the log generated by the dbContext.
Reading somewhere in the web, I found out that in EF6 there is some validation that happens before the sql is passed to the database to be executed. But it seems that in EF Core this is not available anymore. So how can I solve this?
After some research, the only approach I've found to solve this, is implementing some validation by overriding dbContext's SaveChanges method. I've made a merge of these two approaches to build mine:
Implementing Missing Features in Entity Framework Core - Part 3
Validation in EF Core
The result is...
ApplicationDbContext.cs
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
ValidateEntities();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
{
ValidateEntities();
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
private void ValidateEntities()
{
var serviceProvider = this.GetService<IServiceProvider>();
var items = new Dictionary<object, object>();
var entities = from entry in ChangeTracker.Entries()
where entry.State == EntityState.Added || entry.State == EntityState.Modified
select entry.Entity;
foreach (var entity in entities)
{
var context = new ValidationContext(entity, serviceProvider, items);
var results = new List<ValidationResult>();
if (Validator.TryValidateObject(entity, context, results, true)) continue;
foreach (var result in results)
{
if (result == ValidationResult.Success) continue;
var errorMessage = $"{entity.GetType().Name}: {result.ErrorMessage}";
throw new ValidationException(errorMessage);
}
}
}
Note that it's not necessary to override the other SaveChanges overloads, because they call these two.
The Error tells you that youre writing more characters to a field than it can hold.
This error for example would be thrown when you create a given field as NVARCHAR(4) or CHAR(4) and write 'hello' to it.
So you could simply check the length of the values you read in to find the one which is causing your problem. There is at least on which is too long for a field.

How can I create a generic update method for One to Many structures in Entity Framework 5?

I am writing a web application, such that I get different objects back from the web that need to be either updated or added to the database. On top of this, I need to check that the owner is not modified. Since a hacker could potentially get an account and send an update to modify the foreign key to the user model. I don't want to have to manually code all of these methods, instead I want to make a simple generic call.
Maybe something as simple as this
ctx.OrderLines.AddOrUpdateSet(order.OrderLines, a => a.Order)
Based on old persisted records that have a foreign key to Order, and on the new incoming records.
Delete old records that are not on the new records list.
Add new records that are not on the old records list.
Update new records that exist on both lists.
ctx.Entry(orderLine).State=EntityState.Deleted;
...
ctx.Entry(orderLine).State=EntityState.Added;
...
ctx.Entry(orderLine).State=EntityState.Modified;
This gets a bit complicated when the old record is loaded to verify that ownership did not change. I get an error if I don't do.
oldorder.OrderLines.remove(oldOrderLine); //for deletes
oldorder.OrderLines.add(oldOrderLine); //for adds
ctx.Entry(header).CurrentValues.SetValues(header); //for modifications
With Entity Framework 5 there is a new extension function called AddOrUpdate. And there was a very interesting (please read) blog entry on how to create this method before it was added.
I'm not sure if this is too much to ask as a question in StackOverflow, any clues on how to approach the problem may be sufficient. Here are my thoughts so far:
a) leverage AddOrUpdate for some of the functionality.
b) create a secondary context hoping to avoid loading order into the context and avoid extra calls.
c) Set the state of all the saved objects to initially deleted.
Since you have linked to this question from my own question, I thought I'd throw in some newly-aquired experience with Entity Framework for me.
To achieve a common save method in my generic repository with Entity Framework, I do this. (Please note that the Context is a member of my repository, as I am implementing the Unit of Work pattern as well)
public class EFRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
internal readonly AwesomeContext Context;
internal readonly DbSet<TEntity> DbSet;
public EFRepository(AwesomeContext context)
{
if (context == null) throw new ArgumentNullException("context");
Context = context;
DbSet = context.Set<TEntity>();
}
// Rest of implementation removed for brevity
public void Save(TEntity entity)
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
DbSet.Add(entity);
else entry.State = EntityState.Modified;
}
}
Honestly, I can't tell you why this works, because I just kept changing the state conditions - however I do have unit (integration) tests to prove that it works. Hopefully someone more into EF than myself can shed some light on this.
Regarding the "cascading updates", I was curious myself as if it would work using the Unit of Work pattern (my question I linked to was when I did not know it existed, and my repositories would basically create a unit of work whenever I wanted to save/get/delete, which is bad), so I threw in a test case in a simple relational DB. Here is a diagram to give you an idea.
IMPORTANT In order for test case number 2 to work, you need to make your POCO reference properties virtual, in order for EF to provide lazy loading.
The repository implementation is just derived from the generic EFRepository<TEntity> as shown above, so I'll leave out that implementation.
These are my test cases, both pass.
public class EFResourceGroupFacts
{
[Fact]
public void Saving_new_resource_will_cascade_properly()
{
// Recreate a fresh database and add some dummy data.
SetupTestCase();
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
var cultureRepo = new EFCultureRepository(ctx);
var resourceRepo = new EFResourceRepository(cultureRepo, ctx);
var existingCulture = cultureRepo.Get(1); // First and only culture.
var groupToAdd = new ResourceGroup("Added Group");
var resourceToAdd = new Resource(existingCulture,"New Resource", "Resource to add to existing group.",groupToAdd);
// Verify we got a single resource group.
Assert.Equal(1,ctx.ResourceGroups.Count());
// Saving the resource should also add the group.
resourceRepo.Save(resourceToAdd);
ctx.SaveChanges();
// Verify the group was added without explicitly saving it.
Assert.Equal(2, ctx.ResourceGroups.Count());
}
// try creating a new Unit of Work to really verify it has been persisted..
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
Assert.DoesNotThrow(() => ctx.ResourceGroups.First(rg => rg.Name == "Added Group"));
}
}
[Fact]
public void Changing_existing_resources_group_saves_properly()
{
SetupTestCase();
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
ctx.Configuration.LazyLoadingEnabled = true;
var cultureRepo = new EFCultureRepository(ctx);
var resourceRepo = new EFResourceRepository(cultureRepo, ctx);
// This resource already has a group.
var existingResource = resourceRepo.Get(2);
Assert.NotNull(existingResource.ResourceGroup); // IMPORTANT: Property must be virtual!
// Verify there is only one resource group in the datastore.
Assert.Equal(1,ctx.ResourceGroups.Count());
existingResource.ResourceGroup = new ResourceGroup("I am implicitly added to the database. How cool is that?");
// Make sure there are 2 resources in the datastore before saving.
Assert.Equal(2, ctx.Resources.Count());
resourceRepo.Save(existingResource);
ctx.SaveChanges();
// Make sure there are STILL only 2 resources in the datastore AFTER saving.
Assert.Equal(2, ctx.Resources.Count());
// Make sure the new group was added.
Assert.Equal(2,ctx.ResourceGroups.Count());
// Refetch from store, verify relationship.
existingResource = resourceRepo.Get(2);
Assert.Equal(2,existingResource.ResourceGroup.Id);
// let's change the group to an existing group
existingResource.ResourceGroup = ctx.ResourceGroups.First();
resourceRepo.Save(existingResource);
ctx.SaveChanges();
// Assert no change in groups.
Assert.Equal(2, ctx.ResourceGroups.Count());
// Refetch from store, verify relationship.
existingResource = resourceRepo.Get(2);
Assert.Equal(1, existingResource.ResourceGroup.Id);
}
}
private void SetupTestCase()
{
// Delete everything first. Database.SetInitializer does not work very well for me.
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
ctx.Database.Delete();
ctx.Database.Create();
var culture = new Culture("en-US", "English");
var resourceGroup = new ResourceGroup("Existing Group");
var resource = new Resource(culture, "Existing Resource 1",
"This resource will already exist when starting the test. Initially it has no group.");
var resourceWithGroup = new Resource(culture, "Exising Resource 2",
"Same for this resource, except it has a group.",resourceGroup);
ctx.Cultures.Add(culture);
ctx.ResourceGroups.Add(resourceGroup);
ctx.Resources.Add(resource);
ctx.Resources.Add(resourceWithGroup);
ctx.SaveChanges();
}
}
}
It was interesting to learn this, as I was not sure if it would work.
After working on this for a while I found an opensource project called GraphDiff here is it's blog entry 'introducing graphdiff for entity framework code first – allowing automated updates of a graph of detached entities'. I only began using it but it looks impressive. And it does solve the problem of issuing update/delete/insert for Many to One relationships. It actually generalizes the problem to graphs and allows arbitrary nesting.
Here is the generic method I concocted. It does use AddOrUpdate from the System.Data.Entity.Migrations namespace. Which may be reloading records from the db, I'll be checking on that later. The usage is
ctx.OrderLines.AddOrUpdateSet(l => l.orderId == neworder.Id,
l => l.Id, order.orderLines);
Here is the code:
public static class UpdateExtensions
{
public static void AddOrUpdateSet<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, bool>> predicate,
Func<TEntity, int> selector, IEnumerable<TEntity> newRecords) where TEntity : class
{
List<TEntity> oldRecords = set.Where(predicate).ToList();
IEnumerable<int> keys = newRecords.Select(selector);
foreach (TEntity newRec in newRecords)
set.AddOrUpdate(newRec);
oldRecords.FindAll(old => !keys.Contains(selector(old))).ForEach(detail => set.Remove(detail));
}
}

REST PUT method seems to work but doesn't edit an entity

I have a REST Web Service API. I mapped database using JPA. I have an entity "persona". Adding a new entity with POST method works fine, also GET method works fine, but when I try to call PUT method, there is some exception that I found out while debugging: "Cannot suppress a null exception." and "Self-suppression not permitted". In the test database with other entities everything works fine...
Adding works almost the same like editing in my case - firstly I only get the values and then after changing I do everything the same, the only difference is that there is used method PUT instead of POST.
This is my PUT method (here the exception occurs):
#PUT
#Consumes({"application/xml", "application/json"})
public Response edit(Persona entity) {
try {
getJpaController().edit(entity);
return Response.ok().build();
} catch (Exception ex) {
return Response.notModified(ex.getMessage()).build();
}
}
I'm working first time with web services, so I'm a newbie.
What can be the reason of such behaviour? What is this self-suppression error?
If you need any source code else, please, tell me, I will edit my post.
#EDIT:
I found some exception in JpaController Class, my Persona class has:
#OneToOne(cascade = CascadeType.ALL, mappedBy = "persona")
private Personaacceso personaacceso;
In JpaController there is:
Personaacceso personaaccesoOld = persistentPersona.getPersonaacceso();
Personaacceso personaaccesoNew = persona.getPersonaacceso();
if (personaaccesoOld != null && !personaaccesoOld.equals(personaaccesoNew)) {
if (illegalOrphanMessages == null) {
illegalOrphanMessages = new ArrayList<String>();
}
illegalOrphanMessages.add("You must retain Personaacceso " + personaaccesoOld + " since its persona field is not nullable.");
}
So there is showing a message that I have to retain Personaacceso. Any idea how to solve it?
Okey, I know what was the reason now...
Each Persona entity has some other entities (one-to-one or one-to-many). The Jpa Controller wasn't retaining all these containing entities (instead, it was creating new ones). So, the data was lost and because of that persona entity wasn't edited.
Solution:
To every containing entity do something like:
containedEntityNew=containedEntityOld;
However, take into consideration, that then these entity-fields are just rewrited from the old "main" entity.

EF 4 CTP 5: Trouble trying to remove an entity

I have created a model POCO class called Recipe; a corresponding RecipeRepository persists these objects. I am using Code First on top of an existing database.
Every Recipe contains an ICollection<RecipeCategory> of categories that link the Recipes and the Categories table in a many-to-many relationship. RecipeCategory contains the corresponding two foreign keys.
A simplified version of my controller and repository logic looks like this (I have commented out all checks for authorization, null objects etc. for simplicity):
public ActionResult Delete(int id)
{
_recipeRepository.Remove(id);
return View("Deleted");
}
The repository's Remove method does nothing but the following:
public void Remove(int id)
{
Recipe recipe = _context.Recipes.Find(id);
_context.Recipes.Remove(recipe);
_context.SaveChanges();
}
Howevery, the code above does not work since I receive a System.InvalidOperationException every time I run it: Adding a relationship with an entity which is in the Deleted state is not allowed.
What does the error message stand for and how can I solve the problem? The only thing I try to achieve is deleting an entity.
#Ladislav: I have replaced ICollection<RecipeCategory> by ICollection<Category>. Accidentially, ReSharper refactored away the virtual keyword.
However, the problem remains — I cannot delete a Category from a Recipe entity. The following code does not persist the deletion of the categories to the database:
private void RemoveAllCategoriesAssignedToRecipe()
{
foreach (Category category in _recipe.Categories.ToArray())
{
_recipe.Categories.Remove(category);
category.Recipes.Remove(_recipe);
}
_context.SaveChanges();
}
I have debugged the code and can confirm that the collections are modified correctly — that is, they contain no elements after the loop (I have also used the Clear() method). After calling SaveChanges(), they are populated again.
What am I doing wrong?
(Maybe it is important: I am using the Singleton pattern to only have one instance of the context.)
I was able to solve the problem the following way:
private void RemoveAllCategoriesAssignedToRecipe()
{
foreach (Category category in _recipe.Categories.ToArray())
{
Category categoryEntity = _categoryRepository.Retrieve(category.CategoryID);
var recipesAssignedToCategory = categoryEntity.Recipes.ToArray();
categoryEntity.Recipes.Clear();
foreach (Recipe assignedRecipe in recipesAssignedToCategory)
{
if (assignedRecipe.RecipeID == _recipe.RecipeID)
{
continue;
}
categoryEntity.Recipes.Add(assignedRecipe);
}
_context.Entry(categoryEntity).State = EntityState.Modified;
}
_recipe.Categories.Clear();
_context.SaveChanges();
}

Entity Framework and Entity Tracker Problems

If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.