web-dev-qa-db-ja.com

Entity Framework Core:ネストされた値オブジェクトでエンティティを更新できません

値オブジェクトを持つエンティティがあり、この値オブジェクトには別の値オブジェクトがあります。私の問題は、値オブジェクトとともにエンティティを更新すると、親値オブジェクトを持つエンティティが更新されますが、子値オブジェクトは更新されないことです。注、私は最新バージョンのEntity Framework Core 2.1.0-rc1-finalを使用しました。これは親エンティティEmployeeです。

public class Employee : Entity
{
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public string Email { get; private set; }
    public Address Address { get; private set; }
}

これは親値オブジェクトのアドレスです

public class Address : ValueObject<Address>
{
    private Address() { }

    public Address(string street, string city, string state, string country, string zipcode, GeoLocation geoLocation)
    {
        Street = street;
        City = city;
        State = state;
        Country = country;
        ZipCode = zipcode;
        GeoLocation = geoLocation;
    }

    public string Street { get; private set; }
    public string City { get; private set; }
    public string State { get; private set; }
    public string Country { get; private set; }
    public string ZipCode { get; private set; }
    public GeoLocation GeoLocation { get; private set; }
}

これは子値オブジェクトGeoLocationです

public class GeoLocation
{
    private GeoLocation()
    {

    }

    public GeoLocation(decimal longitude, decimal latitude)
    {
        Latitude = latitude;
        Longitude = longitude;
    }
    public Decimal Longitude { get; private set; }
    public Decimal Latitude { get; private set; }
}

従業員を更新するときは、まずデータベースから取得し、次にユーザーインターフェイスから取得した新しい値を使用してAddressプロパティを変更します

var employee = _repository.GetEmployee(empId);
employee.SetAddress(newAddress);

およびSetAddressメソッド

public void SetAddress(Address address)
{
    Guard.AssertArgumentNotNull(address, nameof(address));
    Address = address;
}
6
yo2011

このEF Core GitHubチケット によると、適切に追跡するには、child/nested/ownedタイプのプロパティを直接更新する必要があります。これはEF 2.1で修正される予定でしたが(現在はリリース候補としてのみ利用可能です)、カットが行われていない可能性があります。 2.0.3では、例外の表現を次のように更新しました。

InvalidOperationException:{'ParentID'}の同じキー値を持つ別のインスタンスがすでに追跡されているため、エンティティタイプ 'Parent.Child#Child'のインスタンスは追跡できません。 所有エンティティを置き換える場合は、インスタンスを変更せずにプロパティを変更するか、前に所有していたエンティティエントリを最初に切り離します。

このメッセージの2番目の部分は、DDDを使用している場合に少し発生します。これは、EFが変更を正しく追跡するために子/ネストされたプロパティのプロパティを直接更新する必要があることを示しています(これにより、DDD値オブジェクトが不変であると見なされます)。 GitHubスレッドのコメント のように、コードに一致するように適応された、多少DDDフレンドリーな回避策を提案します。

_public void SetAddress(Address address)
{
    Guard.AssertArgumentNotNull(address, nameof(address));    
    Address.UpdateFrom(address);
}
// And on Address:
internal void UpdateFrom(Address other)
{
    Street = other.Street;
    // ...
}
_

-OR-

2番目に推奨される回避策は、エンティティをデタッチし、Addressのインスタンスを更新してから、再アタッチすることです。私の実装では、この回避策にはあまり運がありませんでしたが、後世のために投稿します。たぶん、私よりも運がいいでしょう。

_context.Entry(employee.Address).State = EntityState.Detached;
employee.SetAddress(newAddress);
context.Entry(employee.Address).State = EntityState.Modified;
_

[〜#〜]更新[〜#〜]

この問題を追跡できるEF Coreチームのオープンチケットをようやく見つけました。 チケット番号10551 目前の問題を具体的に述べており、まだオープンです。それは間違いなくEF Core 2.1に到達せず、Backlog Milestone 3.0に配置されたようです。 EF Coreチームにもっと注目してもらう方法として、この問題に賛成投票できることに注意してください。

UPDATE 2EF Core 2.2では、これをより流動的にする追跡グラフコンポーネントが導入されました。ただし、これには、すべてのEFエンティティがデータベースで生成されたIDを使用する必要があります。このメソッドは、エンティティキーが設定されているかどうかを検査し、エンティティに変更または追加のフラグを付けます。これは削除を含めるように拡張できますが、私の目的ではそのような動作はしたくありません。

_internal void Upsert(object entity)
{
    ChangeTracker.TrackGraph(entity, e =>
    {
        if (e.Entry.IsKeySet)
        {
            e.Entry.State = EntityState.Modified;
        }
        else
        {
            e.Entry.State = EntityState.Added;
        }
    });

    #if DEBUG
    foreach (var entry in ChangeTracker.Entries())
    {
        Debug.WriteLine($"Entity: {entry.Entity.GetType().Name} State: {entry.State.ToString()}");
    }
    #endif
}
_

次に、context.Upsert(<YOUR ENTITY OBJECT>);の前にcontext.SaveChanges();を使用します。

12
bman7716

より簡単な代替手段は、 EFコアの変更されたエンティティに対して永続化されていない所有型プロパティ にあります

要するに、代わりに

_context.Entry(contactModelFromRequestBody).State = EntityState.Modified;

あなたは使うべきです

_context.Update(contactModelFromRequestBody);

0
Emad Gabriel