web-dev-qa-db-ja.com

エンティティとすべてのナビゲーションプロパティの関連付けを再ロードする-DbSetエンティティフレームワーク

エンティティの関連付けの更新に問題があります。次のようなエンティティを取得すると:

MyContext context = new MyContext();

Person myPerson = context.PersonSet.FirstOrDefault();
String myPersonName = myPerson.Name;
Address myPersonAddress = myPerson.Address;

Addressという名前の関連付けとNameという名前のプロパティを持つ人物を取得しました。プロパティの名前など、データベース内のデータを手動で変更した場合、次のコードを使用してエンティティをリロードする必要があります。

context.Entry(myPerson).Reload();

そして、私は名前の新しい値を持っています。しかし、私がアドレスに対して同じことをすると、それは機能しません。これは、Addressが関連プロパティであるためだと思います。更新する必要があります。

Addressアソシエーション(およびPersonクラスの他のすべてのアソシエーション)を強制的に再ロードするにはどうすればよいですか?

編集:

同じケースで、人は複数のアドレスを持つことができます。

MyContext context = new MyContext();

Person myPerson = context.PersonSet.FirstOrDefault();
String myPersonName = myPerson.Name;
List<Address> myPersonAddresses = myPerson.Addresses;

この場合、それは参照ではありません:

context.Entry(myPerson).Reference(p => p.Address).Load();
// Address will be populated with only the new address
// this isn't required because I use lazy loading

しかしコレクション:

context.Entry(myPerson).Collection(p => p.Addresses).Load();
// Address will be populated with old value and new value

これを使用する必要があります:

context.Entry(myPerson).Collection(p => p.Addresses).CurrentValue.Clear();
context.Entry(myPerson).Collection(p => p.Addresses).Load();

しかし、私のすべてのナビゲーションプロパティに対してこれを行うには良い解決策ではないようです。

31
Sam

遅延読み込みを使用しない場合は、(最初​​にAddressを読み込んだときに(たとえば、Includeを使用して)明示的に読み込む必要があったため、新しいPersonを明示的に読み込みます。

context.Entry(myPerson).Reload();
// If the person refers to another Address in the DB
// myPerson.Address will be null now

if (myPerson.Address == null)
    context.Entry(myPerson).Reference(p => p.Address).Load();
    // myPerson.Address will be populated with the new Address now

遅延読み込みを使用する場合、2番目のコードブロックは必要ありません。それにもかかわらず、新しいmyPerson.Addressのプロパティにアクセスするとすぐに、データベースへの新しいクエリを取得します(上記の2番目のコードブロックに新しいクエリがあるように)。最初の行で、ナビゲーションプロパティが読み込まれていないとマークされます。人がDBの新しいアドレスを参照する場合。

この動作は、モデルクラスで外部キーを公開したかどうかには依存しません。

オブジェクトグラフ全体を1回の呼び出しで再読み込みおよび更新する単一の魔法のReloadメソッドを呼び出す方法はないようです(完全なオブジェクトグラフを熱心に読み込む単一のIncludeがないのと同様)。

28
Slauma

ありがとうございました !

context.Entry(myPerson).Collection(p => p.Addresses).Load();

私のためにその仕事をしました。

P.Addressesが1つのエントリを失った場合、次の方法で更新できます。

((IObjectContextAdapter)CurrentContext(context)).ObjectContext.Refresh(RefreshMode.StoreWins, p.Addresses);

しかし、それが1つのエントリを取得した場合は、.Load()メソッドのみが役立ちました。再度、感謝します!

8
Satria

LINQ式を変更するには、 Query() 拡張を使用する必要があります。これは私の個人コードに基づく例です。このコードでは、myPersonオブジェクトの関連するAddressTypeナビゲーションプロパティを使用してAddressesコレクションを再読み込みし、結果をSomeListに配置します。

_DbContext.Entry<Person>(myPerson)
          .Collection(i => i.Adresses) // navigation property for Person
          .Query()
          .Include("AddressType")        // navigation property for Address
          .OrderBy(i => i.Name)
          .ThenBy(i => i.AddressType.AddressTypeName) // just an example
          .Select(i => new someClass
          {
              SoomeField1 = i.SomeField1,
              ...
          })
          .ToList()
          .ForEach(i => SomeList.Add(i)); // SomeList is a List<T>
2
Aleksey Timkov