2

I'm using Entity Framework. I want to load an entity, edit it, and save the changes back in the DB. But no matter if I've edited a foreign key property or a simple property, EF gives me the following error:

Attaching an entity of type 'ClassX' 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.

Please note that ClassX is not a direct virtual property of the class that I'm trying to update, instead it's a virtual property in some of the other classes that my class has navigation properties to them.

I've read some related issues. But I didn't really get how I should apply them to my own problem, since I'm using a generic repository as posted below.

public class GenericRepository<T>  where T : class
{
    private EFDbContext context = new EFDbContext();
    public IEnumerable<T> GetAll()
    {
        return context.Set<T>();
    }
    public void Insert(T entity)
    {
        context.Set<T>().Add(entity);
        context.SaveChanges();
    }
    public void Update(T entity)
    {
        context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();
    }
 //removed for brevity
}

I've encountered another problem related to virtual properties and I was advised to use ViewModels and Object to Object mapping.

As far as I got it, there's 3 options:

  1. Use ViewModels and object-to-object mapping. I'm not going with this one, it was really painful since o2o mapping libraries have lots of bugs.
  2. Somehow uses reference. But I can't do that since the repository is generic. Maybe I should use reflection API for that?
  3. Delete all virtual properties. It is actually an option, since they're creating more problems than they solve.

Can anyone please explain why this problem happens and what's the easiest way to solve it?

Community
  • 1
  • 1
Akbari
  • 2,369
  • 7
  • 45
  • 85
  • 1
    Sounds like you are having issues with updating object graphs. Take a look at GraphDiff for help on that: http://blog.brentmckendrick.com/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/ "mapping libraries have lots of bugs" - I have to disagree. Automapper and the viewmodel pattern are pretty solid techniques. https://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/ – Steve Greene May 20 '15 at 13:39
  • 1
    "Note that if the entity being attached has references to other entities that are not yet tracked, then these new entities will attached to the context in the Unchanged state—they will not automatically be made Modified. If you have multiple entities that need to be marked Modified you should set the state for each of these entities individually." https://msdn.microsoft.com/en-us/data/jj592676.aspx – jjj May 20 '15 at 19:57
  • Thanks for your comments and useful links, but I still don't know how to solve my problem. I'll be thankful if you could explain. How should I change my update method? I'm not going with ViewModels until it's absolutely necessary. I don't want to change or modify associated entities, I just want EF to ignore them! – Akbari May 21 '15 at 02:50
  • Please have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:26

1 Answers1

4

When you set the State of an entity to Modified it also attaches all children (entities referenced by navigation properties) with State == EntityState.Unchanged. If your context already has entities with the same key, it will raise that error.

If you want those entities to be ignored, there are a few options I can think of:

  1. Create a new data context within Update, and don't worry about the children entities because with EntityState.Unchanged, when you call SaveChanges, they'll be ignored. This probably doesn't make sense if you're using some kind of Repository Pattern.
  2. Go through the navigation properties you don't want to attach and set to null before setting State = EntityState.Modified
  3. After setting State = EntityState.Modified, for child entities you want to ignore, set State = EntityState.Detached

Edit

It would also be good to figure out why the context would end up with multiple child entities with the same key in the first place.

jjj
  • 4,822
  • 1
  • 16
  • 39
  • Thanks for your comment. Since my repository is generic, options two and three require some reflection API to use. What about option #1? You mean I should use something like this: `new efdbcontext().entry(entity).state = EntityState.Modified;` and ignore the original context? – Akbari May 23 '15 at 03:13
  • Navigation properties are causing [two](http://stackoverflow.com/questions/30068155/) more [issues](http://stackoverflow.com/questions/29861336/) too. What do you think is the best and easiest approach? – Akbari May 23 '15 at 03:43
  • Well, if we back up to the larger picture, there might be something wrong with how you're implementing this. #1 really might not make sense for your design pattern, though I'm not that familiar with what you're trying to do. It might be helpful to have more information on the how this `GenericRepository` is being used in its lifetime. – jjj May 23 '15 at 04:02
  • I don't have a Unit Of Work pattern, service or something similar, and I'm afraid I can change the app much right now. So in the controllers, I take use of repository for different classes. Please note, that I don't have multiple keys, I don't know where this error is coming from. – Akbari May 23 '15 at 04:12
  • Are you using the same instance of `GenericRepository` to update/insert multiple entities, and the entities happen to reference different objects of the same type (e.g. `ClassX`) and with the same primary key? – jjj May 23 '15 at 04:25
  • I'll create new instance for each class. But, yes, those repositories are general and several actions use them. I don't understand the 2nd question. Each class has one reference to another class, they have just one foreign key to another class. But the thing is, it might be hierarchical, e.g. a => b, c. b => c. – Akbari May 23 '15 at 04:30
  • What I mean is you might be updating `A1` and `A2` with the same `GenericRepository`, but `A1.B` and `A2.B` have the same key, or `A1` might have a collection property `A1.Bs` where multiple `B` instances have the same key. – jjj May 23 '15 at 05:38
  • No, fortunately it's not the case. – Akbari May 23 '15 at 05:49