私はEFにかなり慣れていないので、次の問題で少し立ち往生しているようです..
次のような状況があります。
2 つのアプリケーションが同じデータベースを使用しており、両方が同時にコンテキストをロードして同じ情報を表示しています。
次に、アプリケーション 1 がエンティティを削除し、データベースを savechanges で更新します。次に、アプリケーション 2 が実行され、同じ変更されたエンティティが保存されます。
updategraph を使用してツリーを再帰し、すべてのエンティティをデータベース コンテキストに保存します。ただし、削除されたエンティティを保存する場合、これはデータベースから取得した db コンテキストには存在しませんが、これをデータベースに追加したいと考えています。
ADD を実行しようとしましたが、完全なツリーをコンテキストに追加しようとしているため、多重度エラーが発生します!
アイデアはありますか?
以下は、updategraph 拡張メソッドのコード スニペットです。( http://github.com/refactorthis/GraphDiff )
private static void RecursiveGraphUpdate<T>(DbContext context, T dataStoreEntity, T updatingEntity, UpdateMember member) where T:class
{
if (member.IsCollection)
{
// We are dealing with a collection
var updateValues = (IEnumerable)member.Accessor.GetValue(updatingEntity, null);
var dbCollection = (IEnumerable)member.Accessor.GetValue(dataStoreEntity, null);
var keyFields = context.GetKeysFor(updateValues.GetType().GetGenericArguments()[0]);
var dbHash = MapCollectionToDictionary(keyFields, dbCollection);
// Iterate through the elements from the updated graph and try to match them against the db graph.
List<object> additions = new List<object>();
foreach (object updateItem in updateValues)
{
var key = CreateHash(keyFields, updateItem);
// try to find in db collection
object dbItem;
var itemID = updateItem.GetType().GetProperty("ID");
if (dbHash.TryGetValue(key, out dbItem))
{
var property = updateItem.GetType().GetProperty("Deleted");
bool ysn = property.GetValue(updateItem, null).ToString() == "True" ? true : false;
if (ysn)
{
context.Entry(dbItem).CurrentValues.SetValues(updateItem);
continue;
}
// If we own the collection
if (member.IsOwned)
{
context.Entry(dbItem).CurrentValues.SetValues(updateItem); // match means we are updating
foreach (var childMember in member.Members)
RecursiveGraphUpdate(context, dbHash[key], updateItem, childMember);
}
dbHash.Remove(key); // remove to leave only db removals in the collection
}
else
additions.Add(updateItem);
}
// Removal of dbItem's left in the collection
foreach (var dbItem in dbHash.Values)
{
var property = dbItem.GetType().GetProperty("Deleted");
bool ysn = property.GetValue(dbItem, null).ToString() == "True" ? true : false;
if (ysn)
{
// Own the collection so remove it completely.
if (member.IsOwned)
context.Set(dbItem.GetType()).Remove(dbItem);
dbCollection.GetType().GetMethod("Remove").Invoke(dbCollection, new[] { dbItem });
}
}
// Add elements marked for addition
foreach (object newItem in additions)
{
if (!member.IsOwned)
context.Set(newItem.GetType()).Attach(newItem);
// Otherwise we will add to object
dbCollection.GetType().GetMethod("Add").Invoke(dbCollection, new[] { newItem });
}
}
else // not collection
{
var dbvalue = member.Accessor.GetValue(dataStoreEntity, null);
var newvalue = member.Accessor.GetValue(updatingEntity, null);
if (dbvalue == null && newvalue == null) // No value
return;
// If we own the collection then we need to update the entities otherwise simple relationship update
if (!member.IsOwned)
{
if (dbvalue != null && newvalue != null)
{
var keyFields = context.GetKeysFor(newvalue.GetType());
var newKey = CreateHash(keyFields, newvalue);
var updateKey = CreateHash(keyFields, dbvalue);
if (newKey == updateKey)
return; // do nothing if the same key
}
if (context.Entry(newvalue).State == EntityState.Detached)
context.Set(newvalue.GetType()).Attach(newvalue);
member.Accessor.SetValue(dataStoreEntity, newvalue, null);
context.Entry(newvalue).Reload();
// TODO would like to do this: context.Entry(newvalue).State = EntityState.Unchanged;
// However it seems even though we are in an unchanged state EF will still update the database if the original values are different.
}
else
{
if (dbvalue != null && newvalue != null)
{
// Check if the same key, if so then update values on the entity
var keyFields = context.GetKeysFor(newvalue.GetType());
var newKey = CreateHash(keyFields, newvalue);
var updateKey = CreateHash(keyFields, dbvalue);
// perform update if the same
if (updateKey == newKey)
{
context.Entry(dbvalue).CurrentValues.SetValues(newvalue);
context.Entry(dbvalue).State = EntityState.Modified;
}
else
member.Accessor.SetValue(dataStoreEntity, newvalue, null);
}
else
member.Accessor.SetValue(dataStoreEntity, newvalue, null);
// TODO
foreach (var childMember in member.Members)
RecursiveGraphUpdate(context, dbvalue, newvalue, childMember);
}
}
}