3

次のコードを使用して、my コードから収集した新しい情報でエンティティ オブジェクトを更新しています。Entity Framework 5を使用しています。

次の拡張メソッドを使用します (EF4 で使用していた再接続コードの代わりとして)。

public static void ApplyValues(this object currentObject, object sourceObject, System.Data.Entity.DbContext obj)
{
  obj.Entry(currentObject).CurrentValues.SetValues(sourceObject);
}

問題は、このメソッドが呼び出されると、SetValuesメソッドがアタッチされたオブジェクトの値を変更しようとするためEntityKey(明らかに、これを実行したくありません)、エラーがスローされることです。

ここには2つの質問があると思います:

  1. キー値を更新しようとするのを防ぐ方法はありますか?

  2. ObjectContext.ApplyCurrentValues()そうでない場合、EF4 で正常に動作していたコードを複製するにはどうすればよいですか?

- - アップデート - -

EF4 で使用した以前のコードは次のとおりです。

public static System.Data.Objects.DataClasses.EntityObject ReAttach(this System.Data.Objects.ObjectContext obj, System.Data.Objects.DataClasses.EntityObject objectDetached)
{
    if (objectDetached.EntityKey != null)
    {
        object original = null;
        if (obj.TryGetObjectByKey(objectDetached.EntityKey, out original))
        {
            objectDetached = obj.ApplyCurrentValues(objectDetached.EntityKey.EntitySetName, objectDetached);
            return objectDetached;
        }
        else
        {
            throw new ObjectNotFoundException();
        }
    }
    else
    {
        return objectDetached;
    }
}
4

1 に答える 1

3

私の意見では、この例外は、呼び出し元のコードで何かが間違っているか、少なくとも異常であることを示しています。

currentObjectはアタッチされたエンティティsourceObjectですが、(通常は) 同じキー値を持つ必要がある (またはキー プロパティがまったくない) デタッチされたオブジェクト (エンティティである必要はありません) です。

実際、現在の値DbContextを更新するには、現在接続されているエンティティを明示的に指定する必要があるため、現在の値の設定は とは異なります。ApplyCurrentValuesof youを使用しObjectContextても、このエンティティは提供されません:

objectContext.ApplyCurrentValues("MyEntitySet", sourceObject);

これは違うから…

  • sourceObjectエンティティである必要があり、任意のエンティティにすることはできませんobject
  • と同じキー値を持つ添付エンティティの値を更新します。sourceObject

currentObjectあなたの例では、明らかcurrentObjectに と同じキーを持つエンティティではないため、別のエンティティを更新しsourceObjectます。

ObjectStateEntry.ApplyCurrentChanges( の新しいバージョンに近い) を使用したDbContext場合は、同じ例外が発生します。

var objectContext = ((IObjectContextAdapter)obj).ObjectContext;

var entry = objectContext.ObjectStateManager.GetObjectStateEntry(currentObject);
entry.ApplyCurrentValues(sourceObject);

EF は、キーの値を変更しようとすると文句を言います。sourceObjectそして、がそれを許可するのと同じタイプでない場合は文句を言いcurrentObjectます(私の意見では、プロパティ名が一致する任意のオブジェクト(DTOなど)を使用してエンティティを更新できるためDbContext、プロシージャがより便利になります)。DbContext

編集

EF 4 で使用した方法を再現する際の主な問題は、EF 5/ のエンティティDbContextは派生ではEntityObjectなく、POCO であることです。EntityKeyこのため、このメソッドの一般的な実装を可能にする利用可能なものはありません。

できることは、次のように、エンティティのキ​​ー プロパティをマークするインターフェイスを導入することです。

public interface IEntity
{
    int Id { get; set; }
}

エンティティ クラスは、Orderエンティティなど、このインターフェイスを実装します。

public class Order : IEntity
{
    public int Id { get; set; }
    public DateTime ShippingDate { get; set; }
    // etc.
}

このインターフェイスの制約を持つジェネリック メソッドを作成できます。

public static T ReAttach<T>(DbContext context, T objectDetached)
    where T : class, IEntity
{
    T original = context.Set<T>().Find(objectDetached.Id);
    if (original == null)
        throw new ObjectNotFoundException();

    context.Entry(original).CurrentValues.SetValues(objectDetached);

    return objectDetached;
}

エンティティが常にintプロパティを持っているとは限らないIdが、それらのキーが異なるタイプ、名前、または複合である可能性がある場合は、インターフェイスを使用する代わりに、エンティティのキ​​ーをメソッドに渡す方がおそらく簡単な方法です。

public static T ReAttach<T>(DbContext context, T objectDetached,
    params object[] keyValues) where T : class
{
    T original = context.Set<T>().Find(keyValues);
    if (original == null)
        throw new ObjectNotFoundException();

    context.Entry(original).CurrentValues.SetValues(objectDetached);

    return objectDetached;
}
于 2013-05-20T14:38:10.317 に答える