私のアプリケーションでは、デタッチされたオブジェクトを操作する必要があり、リポジトリ パターンを既に実装しています。問題は、次のコード サンプルに示されています。
var shops = ShopRepository.GetAll(); //Session.Clear() called inside, CacheMode = CacheMode.Ignore
var employees = EmployeeRepository.GetAll(); //same here
// Case 1: Session.Save() works, Session.Merge() irgnores the changes
employees.First().WorksAt.Name = "le new Shop in Town";
EmployeeRepository.Save(employees.First());
// Case 2: Session.Merge() works, Session.Save() throws NonUniqueObjectException
employees.First().WorksAt = employees.Last().WorksAt;
EmployeeRepository.Save(employees.First());
ショップを最初にロードする必要があるため、ユーザーは ComboBox (または同等のもの) を介して従業員の "WorksAt" プロパティを設定できます。
私の期待は、Session.Clear() を呼び出した後、nhibernate がすべてのインスタンス (および基になるグラフも) を「忘れる」ことです。そうではありません。私はまだ Update() - 操作で NonUniqueObjectException を取得し続けています。そのため、オブジェクト グラフに加えられたあらゆる種類の変更を適切に処理しますが、SaveOrUpdate を使用できません。
Session.Merge() は仕事の半分しか完了しません:
例外はスローされず、新しい参照は保存されましたが、shop-properties に加えられた変更は保存されません (マージは単純にキャッシュから「古い」従業員/shop オブジェクトをロードし、employee-properties を古いインスタンスにコピーするため)
私がすでに試したこと:
- カスケードを「マージ」に設定
- 構成で第 2 レベルのキャッシュを無効にする
- Session.Lock(myObj, LockMode.None)
- 自作の半マージ: Session.Merge() によって返されたオブジェクトの Repository.Save() のリフレクションを介してすべての参照をリセットします
- Equals() および GetHashCode() メソッドをオーバーライドする
モデルには最大 50 個のエンティティが含まれているため、保存/更新時にセッション内で参照されているすべての bo-instance を (再) ロードすることはオプションではありません。現在、nHibernate 3.3.0.4 と FluentNHibernate 1.3 を使用しています。
両方のケースをカバーするコンパクトでエレガントなソリューションはありますか? NHibernate の内部 ID 追跡を無効にすると、問題が解決するのでしょうか?
マッピングは次のようになります。
public class Shop
{
public virtual Int Id { get; set; };
public virtual string Name { get; set; };
public virtual ISet<Employee> AllEmployees{ get; set; }
public Shop()
{
AllEmployees = new HashedSet<Employee>();
}
}
public class ShopMap: ClassMap<Shop>
{
public ShopMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
HasMany(c => c.AllEmployees).Cascade.None().NotFound.Ignore();
}
public class Employee
{
public virtual Int Id { get; set; };
public virtual string FirstName { get; set; };
public virtual string SureName { get; set; };
public virtual Shop WorksAt{ get; set; }
public Employee()
{
}
}
public class EmployeeMap: ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.SureName);
References(c => c.WorksAt).Cascade.SaveUpdate().Fetch.Join().NotFound.Ignore();
}
}
アップデート
リポジトリ基本クラス内の Save() メソッドのコードは次のとおりです。
public static void Save(T pSaveObj)
{
using (ISession session = GetSession())
{
using (ITransaction trans = session.BeginTransaction())
{
// session.Save(pSaveObj);
session.Save(session.Merge(pSaveObj));
session.Flush();
session.Clear();
}
}
}