I am using AutoMapping in FNH 1.0 (with Tom Cabanski's modified #arch from 9/2009) to persist a parent-child relationship.
The parent and child, Managers and Employees, are both of type User. Managers has a collection of DirectReports of IList because a Manager can have another manager as a direct report.
I have a test where I instantiate three Employees and a Manager, I add the three Employees to the Manager's DirectReports collection and then save (flush and evict) the Manager. I should, this point, be able to retrieve any of the employees, despite not directly having saved them. My mapping clearly states that Cascade.SaveUpdate() on the HasMany of DirectReports.
The only time I can get cascading saves to work, is to remove inheritance from all of the classes and make the Manager.DirectReports signature of IList. That flat out won't do.
Is there something about Cascades in NHibernate that I am missing?
Here is the code:
[TestFixture]
public class can_cascade_saves : RepositoryBehaviorSpecificationTestsBase
{
#region Overrides of BehaviorSpecificationTestsBase
private Manager manager;
private Employee employee1, employee2, employee3;
private ManagerRepository userRepository = new ManagerRepository();
private EmployeeRepository _employeeRepository = new EmployeeRepository();
protected override void EstablishContext()
{
manager = new Manager{Name = "Mort"};
employee1 = new Employee { Name = "Gort" };
employee2 = new Employee { Name = "Bort" };
employee3 = new Employee { Name = "Jort" };
}
protected override void Act()
{
manager.Employees.Add(employee1);
manager.Employees.Add(employee2);
manager.Employees.Add(employee3);
userRepository.SaveOrUpdate(manager);
FlushSessionAndEvict(manager); // I see ONE insert, not four. A later test reveals no employee1 object.
}
...
Mapping and classes are as follows...
public abstract class User : Entity
{
public virtual string Name { get; set; }
public virtual Manager Boss { get; protected set; }
}
public class Employee : User
{
}
public class Manager : User
{
private IList<User> _employees = new List<User>();
public virtual IList<User> Employees
{
get { return _employees; }
protected set { _employees = value; }
}
}
public class UserMap : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Map(c => c.Name);
mapping.DiscriminateSubClassesOnColumn<int>("UserType");
}
}
public class EmployeeSubclassMap : SubclassMap<Employee>
{
public EmployeeSubclassMap()
{
DiscriminatorValue(2);
}
}
public class ApproverSubclassMap : SubclassMap<Manager>
{
public ApproverSubclassMap()
{
DiscriminatorValue(1);
HasMany(c => c.Employees)
.Cascade.SaveUpdate()
.Inverse();
}
}
And this DOES work....
public class Employee : Entity
{
public virtual string Name { get; set; }
public virtual Manager Boss { get; protected set; }
}
public class Manager : Entity
{
public virtual string Name { get; set; }
public virtual Manager Boss { get; protected set; }
private IList<Employee> _employees = new List<Employee>();
public virtual IList<Employee> Employees
{
get { return _employees; }
protected set { _employees = value; }
}
}
public class EmployeeMap :IAutoMappingOverride<Employee>
{
public void Override(AutoMapping<Employee> mapping)
{
mapping.Map(c => c.Name);
mapping.References(c => c.Boss)
.Cascade.SaveUpdate();
}
}
public class ManagerMap :IAutoMappingOverride<Manager>
{
public void Override(AutoMapping<Manager> mapping)
{
mapping.Map(c => c.Name);
mapping.References(c => c.Boss)
.Cascade.SaveUpdate();
mapping.HasMany(c => c.Employees)
.Access.CamelCaseField(Prefix.Underscore)
.Inverse()
.Cascade.SaveUpdate();
}
}
[TestFixture]
public class can_cascade_saves : RepositoryBehaviorSpecificationTestsBase
{
#region Overrides of BehaviorSpecificationTestsBase
private Manager manager;
private Employee employee1, employee2, employee3;
private ManagerRepository userRepository = new ManagerRepository();
private EmployeeRepository _employeeRepository = new EmployeeRepository();
protected override void EstablishContext()
{
manager = new Manager{Name = "Mort"};
employee1 = new Employee { Name = "Bort" };
employee2 = new Employee { Name = "Gort" };
employee3 = new Employee { Name = "Jort" };
}
protected override void Act()
{
manager.Employees.Add(employee1);
manager.Employees.Add(employee2);
manager.Employees.Add(employee3);
userRepository.SaveOrUpdate(manager);
FlushSessionAndEvict(manager); // <-- of course, it saves all as advertised
}
Update - I should have added that all I am trying to map is a composite pattern where my component is User, Composite role is played by Manager, and my leaf is Employee. Can that be done?