In my application i need to work with detached objects and have already implemented the reporitory pattern. the problems show up in the following code sample:
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());
The shops need to be loaded first, so the user may set the "WorksAt" - Property for employees via a ComboBox (or something comparable).
My expectation is, that nhibernate "forgets" all instances (and the underlying graph also) after calling Session.Clear(). That is not the case. I still keep getting the NonUniqueObjectException on Update() - operations. So, i'm not able to use SaveOrUpdate although it handles all kind of changes made to the object graph quite well.
Session.Merge() will only get half the job done:
no exceptions were thrown, new references were saved, but changes made to shop-properties don't get saved (because merge simply loads the "old" employee / shop objects from the cache and copies the employee-properties to the old instance)
What i've already tried:
- Set cascade to "merge"
- Disable 2nd level cache in configuration
- Session.Lock(myObj, LockMode.None)
- Selfmade semi-merge: reset all the references via reflection on Repository.Save() for the object returned by Session.Merge()
- Override Equals() and GetHashCode() methods
The model contains ~ 50 entities, so (re)loading every referenced bo-instance within a session on saving / updating is not an option. Currently i am using nHibernate 3.3.0.4 and FluentNHibernate 1.3.
Is there a compact / elegant solution that covers up both cases? Maybe disabling NHibernate's internal id-tracking will solve the issues?
The mapping may look like this:
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();
}
}
Update
Here's the code of my Save() method inside the repository base-class:
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();
}
}
}