Many to Many relationship with Fluent NHibernate

fluent-nhibernatemany-to-many

I'm getting the following error: "Can't figure out what the other side of a many-to-many should be."
Team Entity:

public class Team : IEntity
{
    public int Id { get; set; }

    public string Name { get; set; }

    public IList<Employee> Employee { get; set; }

    public Team()
    {
        Employee = new List<Employee>();
    }
}

Employee Entity:

public class Employee : IEntity
{
    public int Id { get; set; }

    public String LastName { get; set; }

    public string FirstName { get; set; }

    public IList<Team> Team { get; set; }

    public string EMail { get; set; }

    public Employee()
    {
        Team = new List<Team>();
    }
}

Team mapping:

public class TeamMap : ClassMap<Team>
{
    public TeamMap()
    {
        // identity mapping
        Id(p => p.Id);

        // column mapping
        Map(p => p.Name);

        // relationship mapping
        HasManyToMany<Employee>(m => m.Employee);
    }
}

Employee mapping:

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        // identifier mapping
        Id(p => p.Id);

        // column mapping
        Map(p => p.EMail);
        Map(p => p.LastName);
        Map(p => p.FirstName);

        // relationship mapping
        HasManyToMany<Team>(m => m.Team);
    }
}

Nobody has an answer?

Edit: The error occurs on the following code:

public static ISessionFactory CreateSessionFactory()
{
    return Fluently.Configure()
        .Database(MsSqlConfiguration.MsSql2008
        .ConnectionString(c=>
            c.Database("Ariha")
            .TrustedConnection()
            .Server("localhost")
            ).ShowSql())
        .Mappings(m => m.FluentMappings
            .AddFromAssemblyOf<BookMap>()
            .AddFromAssemblyOf<MagazineMap>()
            .AddFromAssemblyOf<EmployeeMap>()
            .AddFromAssemblyOf<TeamMap>())
        .ExposeConfiguration(BuildSchema)
        .BuildSessionFactory();
}

edit: here the whole solution: http://rapidshare.com/files/309653409/S.O.L.I.D.Ariha.rar.html

Best Answer

Could you provide code that causes your error? I just tried your mappings and they seem to work fine (on fluent 1.0 RTM and NH 2.1.1 GA with an SQLite database), with a minor modification to your EmployeeMap (I have assumed the employee-team relationship is bidirectional, and as per documentation you need to mark one side as inverse).

 // relationship mapping
 HasManyToMany<Team>(m => m.Team).Inverse();

Of course if the employee-team relationship is not bidirectional, I would have thought you should be able to specify a different .Table(name) for each one - but I have not tested this and you seem to be getting different results anyway (hence why providing example code would be best)

I'd also add that I suspect Set semantics (instead of Bag) would be more appropriate for the Employee.Team and Team.Employee properties. (Irregardless, don't do anything that assumes order is preserved, there is no guarantee that it will be)

Suggested mapping and example:

 public class Team
 {
  public int Id { get; set; }
  public string Name { get; set; }
  public ICollection<Employee> Employee { get; set; }
  public Team() { Employee = new List<Employee>(); }
 }

 public class Employee
 {
  public int Id { get; set; }
  public String LastName { get; set; }
  public string FirstName { get; set; }
  public ICollection<Team> Team { get; set; }
  public string EMail { get; set; }
  public Employee() { Team = new List<Team>(); }
 }

 public class TeamMap : ClassMap<Team>
 {
  public TeamMap()
  {
   Not.LazyLoad();
   // identity mapping
   Id(p => p.Id);
   // column mapping
   Map(p => p.Name);
   // relationship mapping
   HasManyToMany<Employee>(m => m.Employee).AsSet();
  }
 }

 public class EmployeeMap : ClassMap<Employee>
 {
  public EmployeeMap()
  {
   Not.LazyLoad();
   // identifier mapping
   Id(p => p.Id);
   // column mapping
   Map(p => p.EMail);
   Map(p => p.LastName);
   Map(p => p.FirstName);
   // relationship mapping
   HasManyToMany<Team>(m => m.Team).Inverse().AsSet();
  }
 }

 [TestFixture]
 public class Mapping
 {
  [Test]
  public void PersistDepersist()
  {
   var fcfg = Fluently.Configure()
    .Database(SQLiteConfiguration.Standard.UsingFile("testdb.sqldb"))
    .Mappings(mc =>
    {
     mc.FluentMappings.Add(typeof (TeamMap));
     mc.FluentMappings.Add(typeof (EmployeeMap));
    })
    .ExposeConfiguration(cfg => new SchemaExport(cfg).Execute(false, true, false));

   var sess = fcfg.BuildSessionFactory().OpenSession();


   var teams = Enumerable.Range(0, 4).Select(i => new Team() {Name = "Team " + i}).ToArray();
   var employees = Enumerable.Range(0, 10).Select(i => new Employee() {FirstName = "Employee " + i}).ToArray();

   teams[0].Employee = new List<Employee>() {employees[0], employees[3], employees[5]};
   teams[1].Employee = new List<Employee>() {employees[7], employees[2], employees[5]};
   teams[3].Employee = new List<Employee>() {employees[0], employees[8], employees[9]};

   foreach (var team in teams)
    foreach (var employee in team.Employee)
     employee.Team.Add(team);

   Console.WriteLine("Dumping Generated Team/Employees:");
   Dump(teams);
   Dump(employees);

   using (var t = sess.BeginTransaction())
   {
    foreach (var team in teams)
     sess.Save(team);
    foreach (var employee in employees)
     sess.Save(employee);
    t.Commit();
   }

   sess.Flush();
   sess.Clear();

   var teamsPersisted = sess.CreateCriteria(typeof (Team)).List<Team>();
   var employeesPersisted = sess.CreateCriteria(typeof (Employee)).List<Employee>();

   Assert.AreNotSame(teams, teamsPersisted);
   Assert.AreNotSame(employees, employeesPersisted);

   Console.WriteLine();
   Console.WriteLine();
   Console.WriteLine("Dumping Depersisted Team/Employees:");
   Dump(teamsPersisted);
   Dump(employeesPersisted);
  }

  private static void Dump(IEnumerable<Team> teams)
  {
   foreach (var team in teams)
    Console.WriteLine("Team: " + team.Name + " has members: " + string.Join(", ", team.Employee.Select(e => e.FirstName).ToArray()));
  }

  private static void Dump(IEnumerable<Employee> employees)
  {
   foreach (var employee in employees)
    Console.WriteLine("Employee: " + employee.FirstName + " in teams: " + string.Join(", ", employee.Team.Select(e => e.Name).ToArray()));
  }
 }
Related Topic