4

I have a repository like that:

public class Repository<T> : IRepository<T> where T : class 
{
    private readonly IRepositoryContext _repositoryContext;

    public Repository(IRepositoryContext repositoryContext)
    {
        _repositoryContext = repositoryContext;
        _objectSet = repositoryContext.GetObjectSet<T>();
    }

    public virtual void Update(T entity)
    {
        ObjectSet.AddObject(entity);
        _repositoryContext.ObjectContext.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
        _repositoryContext.SaveChanges();
    }
  }

Now that actually works for all scalar properties of the entity, but all the other entities that associated with properties of entity typeOf(T), don't care that entity state is modified, and EF simply adds new data.

So, if you do for example Repository<Student>.Update(), and you only changed the name, it will find the right Student and change his name, but it also will change the Campus, although you already have a Campus associated with that student, it will be created again with a different CampusId.

Show me please the correct way to do updates in this situation.

iLemming
  • 34,477
  • 60
  • 195
  • 309
  • Welcome in the club of EF-Users-having-the-pain-to-update-relationships-and-related-entities. You probably won't find a *generic* solution for an `Update` method. It's one of the main reasons why generic repositories alone in EF are so useless (unless as internal helpers inside of aggregate repositories perhaps). – Slauma Jul 27 '11 at 21:10
  • 2
    There is [no correct way](http://stackoverflow.com/questions/3635071/update-relationships-when-saving-changes-of-ef4-poco-objects/3635326#3635326). Simply generic approach doesn't work in this case. – Ladislav Mrnka Jul 27 '11 at 21:24
  • well, could you show me then non-generic approach for the situation where I don't actually operate with entities directly. Instead I map them to ViewModel classes through AutoMapper – iLemming Jul 27 '11 at 21:38
  • 1
    Here are code examples how to update a detached entity which has a single child collection: http://stackoverflow.com/q/5538974/270591. Basically: Load original object graph from DB, compare with your changed detached object graph, apply all the changes you detect to the original and save. It's for DbContext, but the idea is the same for ObjectContext. It becomes much more complicated for a more complex object graph (with multiple collections and grand child collections and so on). There are possibly other ways but probably not much easier ways. – Slauma Jul 27 '11 at 22:04

1 Answers1

4

What I did when I wanted to follow generic approach was translated to your code something like:

public class Repository<T> : IRepository<T> where T : class 
{
    ...

    public virtual void Update(T entity)
    {
        if (context.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Detached)
        {
            throw new InvalidOperationException(...);
        }

        _repositoryContext.SaveChanges();
    }
}

All my code then worked like:

var attachedEntity = repository.Find(someId);
// Merge all changes into attached entity here
repository.Update(attachedEntity);

=> Doing this in generic way moves a lot of logic into your upper layer. There is no better way how to save big detached object graphs (especially when many-to-many relations are involved and deleting of relations is involved).

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • 3
    The problem with this solution is that it only takes into account Update, but the same problem happens for Insert on a new object. If you insert an object that is linked to other detached entities which already exist in the database (which may in turn be linked to other entities/sub-entities), all those entities will be recreated. What a nightmare. This seems like such a needless behaviour especially since those detached objects all have hydrated ID properties so it would be really easy to detect that they have already been loaded in a prior call. – Marchy Mar 25 '12 at 03:54