Obiekt z tym samym kluczem już istnieje w Menedżerze ObjectStateManager. Menedżer ObjectStateManager nie może śledzić wielu obiektów za pomocą tego samego klucza

Używanie EF5 z generycznym wzorcem repozytorium i ninject do wstrzykiwania zależności i napotkanie problemu podczas próby aktualizacji encji do bazy danych przy użyciu przechowywanych proców z moim edmx.

Moja aktualizacja w DbContextRepository.cs to:
public override void Update(T entity)
{
    if (entity == null)
        throw new ArgumentException("Cannot add a null entity.");

    var entry = _context.Entry<T>(entity);

    if (entry.State == EntityState.Detached)
    {
        _context.Set<T>().Attach(entity);
        entry.State = EntityState.Modified;
    }
}
Z mojego adresu.cs który wraca do mojego repozytorium mam:
 public int Save(vw_address address)
{
    if (address.address_pk == 0)
    {
        _repo.Insert(address);
    }
    else
    {
        _repo.Update(address);
    }

    _repo.SaveChanges();

    return address.address_pk;
}

Kiedy uderzy w Attach i EntityState.Zmodyfikował go z błędem:

Obiekt o tym samym kluczu już istnieje w ObjectStateManager. Menedżer ObjectStateManager nie może śledzić wielu obiektów za pomocą tego samego klucza.

Przejrzałem wiele sugestii w stosie i w Internecie i nie wymyśliłem niczego, co by to rozwiązało. Każda praca będzie mile widziana.

Dzięki!
Author: Chris Schiffhauer, 2012-09-25

10 answers

Edit : oryginalna odpowiedź używana Find zamiast Local.SingleOrDefault. DziaĹ ' aĹ 'a w poĹ' Ä ... czeniu z metodÄ ... Save @Juan, ale moĹźe spowodowaÄ ‡ niepotrzebne zapytania do bazy danych i else czÄ ™ Ĺ "Ä ‡ prawdopodobnie nigdy nie zostaĹ' a wykonana (wykonanie czÄ ™ Ĺ "ci else spowodowaĺ' oby wyjÄ ... tek, poniewaĹź Find juĹź zapytaĺ 'bazÄ ™ danych i nie znalazĹ' encji, wiÄ ™ c nie moĹźna jej zaktualizować). Dzięki @BenSwayne za znalezienie problemu.

Musisz sprawdzić, czy encja z tym samym kluczem jest już śledzona przez kontekst i Modyfikuj tę encję zamiast dołączać bieżącą:

public override void Update(T entity) where T : IEntity {
    if (entity == null) {
        throw new ArgumentException("Cannot add a null entity.");
    }

    var entry = _context.Entry<T>(entity);

    if (entry.State == EntityState.Detached) {
        var set = _context.Set<T>();
        T attachedEntity = set.Local.SingleOrDefault(e => e.Id == entity.Id);  // You need to have access to key

        if (attachedEntity != null) {
            var attachedEntry = _context.Entry(attachedEntity);
            attachedEntry.CurrentValues.SetValues(entity);
        } else {
            entry.State = EntityState.Modified; // This should attach entity
        }
    }
}  

Jak widać głównym problemem jest to, że metoda SingleOrDefault musi znać klucz, aby znaleźć encję. Możesz stworzyć prosty interfejs odsłaniający klucz (IEntity w moim przykładzie) i zaimplementować go we wszystkich swoich obiektach, które chcesz przetworzyć w ten sposób.

 125
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-07 02:10:43

Nie chciałem zanieczyszczać moich automatycznie generowanych klas EF poprzez dodawanie interfejsów lub atrybutów. tak więc jest to naprawdę trochę z niektórych powyższych odpowiedzi (więc kredyt idzie do Ladislav Mrnka). To było dla mnie proste rozwiązanie.

Dodałem func do metody update, która znalazła klucz integer encji.

public void Update(TEntity entity, Func<TEntity, int> getKey)
{
    if (entity == null) {
        throw new ArgumentException("Cannot add a null entity.");
    }

    var entry = _context.Entry<T>(entity);

    if (entry.State == EntityState.Detached) {
        var set = _context.Set<T>();
        T attachedEntity = set.Find.(getKey(entity)); 

        if (attachedEntity != null) {
            var attachedEntry = _context.Entry(attachedEntity);
            attachedEntry.CurrentValues.SetValues(entity);
        } else {
            entry.State = EntityState.Modified; // This should attach entity
        }
    }
}  

Wtedy, gdy wywołujesz swój kod, możesz użyć..

repository.Update(entity, key => key.myId);
 8
Author: Geoff Wells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-11 21:01:17

Można rzeczywiście wycofać Id poprzez refleksję, patrz przykład poniżej:

        var entry = _dbContext.Entry<T>(entity);

        // Retreive the Id through reflection
        var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity);

        if (entry.State == EntityState.Detached)
        {
            var set = _dbContext.Set<T>();
            T attachedEntity = set.Find(pkey);  // access the key
            if (attachedEntity != null)
            {
                var attachedEntry = _dbContext.Entry(attachedEntity);
                attachedEntry.CurrentValues.SetValues(entity);
            }
            else
            {
                entry.State = EntityState.Modified; // attach the entity
            }
        }
 7
Author: Toffee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-31 14:49:47

@serj-sagan powinieneś to zrobić w ten sposób:

**zauważ, że YourDb powinno być klasą pochodzącą z DbContext.

public abstract class YourRepoBase<T> where T : class
{
    private YourDb _dbContext;
    private readonly DbSet<T> _dbset;

    public virtual void Update(T entity)
    {
        var entry = _dbContext.Entry<T>(entity);

        // Retreive the Id through reflection
        var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity);

        if (entry.State == EntityState.Detached)
        {
           var set = _dbContext.Set<T>();
           T attachedEntity = set.Find(pkey);  // access the key
           if (attachedEntity != null)
           {
               var attachedEntry = _dbContext.Entry(attachedEntity);
               attachedEntry.CurrentValues.SetValues(entity);
           }
           else
           {
              entry.State = EntityState.Modified; // attach the entity
           }
       }
    }

}

 2
Author: Toffee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:15

Innym rozwiązaniem (na podstawie odpowiedzi @Sergey) może być:

private void Update<T>(T entity, Func<T, bool> predicate) where T : class
{
    var entry = Context.Entry(entity);
    if (entry.State == EntityState.Detached)
    {
        var set = Context.Set<T>();
        T attachedEntity = set.Local.SingleOrDefault(predicate); 
        if (attachedEntity != null)
        {
            var attachedEntry = Context.Entry(attachedEntity);
            attachedEntry.CurrentValues.SetValues(entity);
        }
        else
        {
            entry.State = EntityState.Modified; // This should attach entity
        }
    }
}

I wtedy nazwałbyś to tak:

Update(EntitytoUpdate, key => key.Id == id)
 1
Author: Andrés S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 19:04:52

Bez refleksji i jeśli nie chcesz używać interfejsów, możesz użyć delegatów funkcyjnych, aby znaleźć encję w bazie danych. Oto zaktualizowana próbka z góry.

private void Update<T>(T entity, Func<ObservableCollection<T>, T> locatorMap) where T : class
{
    var entry = Context.Entry(entity);
    if (entry.State == EntityState.Detached)
    {
        var set = Context.Set<T>();
        T attachedEntity = locatorMap(set.Local); 

        if (attachedEntity != null)
        {
            var attachedEntry = Context.Entry(attachedEntity);
            attachedEntry.CurrentValues.SetValues(entity);
        }
        else
        {
            entry.State = EntityState.Modified; // This should attach entity
        }
    }
}

Nazwałbyś to tak:

Update(EntitytoUpdate, p => p.SingleOrDefault(a => a.Id == id))
 0
Author: Sergey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-15 04:32:58

Jeśli ustawisz swój kontekst na asnotracking (), to aspmvc przestanie śledzić zmiany encji w pamięci (co i tak jest pożądane w Internecie).

_dbContext.Products.AsNoTracking().Find(id);  

Polecam przeczytać więcej na ten temat na http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application

 0
Author: Yashvit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-23 17:52:44

Odłączenie znalezionego bytu (Patrz attachedEntity W rozwiązanie Ladislava ) i ponowne dołączenie zmodyfikowanego zadziałało dla mnie dobrze.

Rozumowanie za tym jest proste: jeśli coś jest niezmienne, to zastąp to (jako całość, byt) z miejsca, gdzie należy, pożądanym.

Oto przykład jak to zrobić:

var set = this.Set<T>();
if (this.Entry(entity).State == EntityState.Detached)
{
    var attached = set.Find(id);
    if (attached != null) { this.Entry(attached).State = EntityState.Detached; }
    this.Attach(entity);
}

set.Update(entity);

Oczywiście można łatwo zorientować się, że ten fragment jest częścią metody generycznej, stąd użycie T, która jest parametrem szablonu, i Set<T>().

 0
Author: Alexander Christov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 06:38:27

Powyższą odpowiedzią może być EF 4.1+. Dla tych NA 4.0, spróbuj tej prostej metody...nie do końca przetestowany, ale załączył i zapisał moje zmiany.

    public void UpdateRiskInsight(RiskInsight item)
    {
        if (item == null)
        {
            throw new ArgumentException("Cannot add a null entity.");
        }

        if (item.RiskInsightID == Guid.Empty)
        {
            _db.RiskInsights.AddObject(item);
        }
        else
        {
            item.EntityKey = new System.Data.EntityKey("GRC9Entities.RiskInsights", "RiskInsightID", item.RiskInsightID);
            var entry = _db.GetObjectByKey(item.EntityKey) as RiskInsight;
            if (entry != null)
            {
                _db.ApplyCurrentValues<RiskInsight>("GRC9Entities.RiskInsights", item);
            }

        }

        _db.SaveChanges();

    }
 -2
Author: thomas gathings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-28 03:46:05

Być może zapomniałeś zainstalować obiekt fBLL = new FornecedorBLL(); w algun place

 -3
Author: Junior Ramoty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-25 01:27:06