71

一般的なリポジトリ パターンで EF5 を使用し、依存性注入に ninject を使用すると、edmx でストアド プロシージャを使用してエンティティをデータベースに更新しようとすると問題が発生します。

DbContextRepository.cs の私の更新は次のとおりです。

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;
    }
}

リポジトリに戻る AddressService.cs から、次のものがあります。

 public int Save(vw_address address)
{
    if (address.address_pk == 0)
    {
        _repo.Insert(address);
    }
    else
    {
        _repo.Update(address);
    }

    _repo.SaveChanges();

    return address.address_pk;
}

Attach および EntityState.Modified にヒットすると、次のエラーが表示されます。

同じキーを持つオブジェクトが ObjectStateManager に既に存在します。ObjectStateManager は、同じキーを持つ複数のオブジェクトを追跡できません。

私は、スタック内およびインターネット上の多くの提案に目を通しましたが、それを解決するものは何も思いつきませんでした。任意の回避策をいただければ幸いです。

ありがとう!

4

10 に答える 10

125

編集Find:の代わりに使用される元の回答Local.SingleOrDefault。@JuanのSave方法と組み合わせて機能しましたが、データベースへの不要なクエリが発生する可能性があり、elseおそらく実行されなかった部分がありました(Findがすでにデータベースにクエリを実行し、エンティティが見つからなかったため、else部分を実行すると例外が発生し、更新できませんでした) . 問題を見つけてくれた@BenSwayneに感謝します。

同じキーを持つエンティティがコンテキストによって既に追跡されているかどうかを確認し、現在のエンティティをアタッチする代わりにそのエンティティを変更する必要があります。

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
        }
    }
}  

ご覧のとおり、主な問題は、SingleOrDefaultメソッドがエンティティを見つけるためのキーを知る必要があることです。キーを公開する単純なインターフェイスを作成し(IEntity私の例では)、この方法で処理するすべてのエンティティに実装できます。

于 2012-09-25T17:03:15.763 に答える
8

インターフェイスや属性を追加して、自動生成された EF クラスを汚染したくありませんでした。したがって、これは実際には上記の回答の一部から少しだけ抜粋したものです (クレジットは Ladislav Mrnka に与えられます)。これは私にとって簡単な解決策を提供しました。

エンティティの整数キーを見つける関数を update メソッドに追加しました。

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
        }
    }
}  

次に、コードを呼び出すときに、..

repository.Update(entity, key => key.myId);
于 2014-02-28T12:22:30.717 に答える
7

実際には、リフレクションを通じて Id を取得できます。以下の例を参照してください。

        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
            }
        }
于 2013-02-20T07:18:01.517 に答える
2

@ serj-saganこの方法で行う必要があります。

**YourDb は 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
           }
       }
    }

}

于 2013-10-31T14:46:29.660 に答える
0

リフレクションがなく、インターフェイスを使用したくない場合は、関数デリゲートを使用してデータベース内のエンティティを見つけることができます。上記から更新されたサンプルを次に示します。

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
        }
    }
}

次のように呼び出します。

Update(EntitytoUpdate, p => p.SingleOrDefault(a => a.Id == id))
于 2013-12-15T03:46:16.463 に答える
-2

上記の答えはEF4.1+かもしれません。4.0を使用している場合は、この簡単な方法を試してください...実際にはテストされていませんが、変更を添付して保存しました。

    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();

    }
于 2012-12-28T03:46:05.557 に答える
-3

fBLL = new FornecedorBLL();algun の場所にオブジェクトをインストールするのを忘れた可能性があります

于 2016-08-24T23:00:39.660 に答える