I'm beginning to learn NHibernate so am trying to create some simple relationships - I am trying to create the relationships between a team and the players & manager - so far I have 2 classes:
public class Person
{
public virtual int PersonId { get; set; }
public virtual Team Team { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual PersonType PersonType { get; set; } = PersonType.Player;
}
public enum PersonType
{
Player = 1,
Manager = 2,
Referee = 3
}
public class Team
{
public virtual int TeamId { get; set; }
public virtual int Ranking { get; set; }
public virtual IList<Person> Players { get; set; } = new List<Person>();
public virtual Person Manager { get; set; }
}
as well as the class maps for each:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.PersonId);
References(x => x.Team).Cascade.None();
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.PersonType).CustomType<PersonType>(); // register the enum as a custom type, otherwise it will be mapped as a string
}
}
public class TeamMap : ClassMap<Team>
{
public TeamMap()
{
Id(x => x.TeamId);
Map(x => x.Ranking);
HasMany(x => x.Players).Inverse().Cascade.All(); // any changes that are made to the team are cascaded to the players
References(x => x.Manager).Cascade.None();
}
}
When I am running the following code in a Console application
class Program
{
static void Main(string[] args)
{
// pull a connection string from the config file
var connectionString = ConfigurationManager.ConnectionStrings["Bonkers"].ConnectionString;
// build a configuration object
var configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012.ConnectionString(connectionString))
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<PersonMap>()
.AddFromAssemblyOf<TeamMap>())
.BuildConfiguration();
// export the schema configuration
var exporter = new SchemaExport(configuration);
exporter.Execute(true, true, false);
// create a session factory
var sessionFactory = configuration.BuildSessionFactory();
// open a new session and add a new customer
using (var session = sessionFactory.OpenSession())
{
var manager = new Person { FirstName = "Chris", PersonType = PersonType.Manager };
var players = new List<Person>()
{
manager,
new Person { FirstName = "Mark", PersonType = PersonType.Player },
new Person { FirstName = "Karl", PersonType = PersonType.Player }
};
players.ForEach(player => session.Save(player)); // save all the players (& manager)
var team = new Team { Ranking = 1, Manager = manager, Players = players.Where(p => p.PersonType != PersonType.Manager).ToList() };
players.ForEach(player => session.Save(player)); // save all the players (& manager)
session.Save(team); // save the team (the players should now exist in there)
}
// using a session query the database and update the customer type
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.Query<Team>().ToList().ForEach(team =>
{
WriteLine($"Manager: {team.Manager.FirstName} Ranking: {team.Ranking}");
WriteLine($"Team players:");
team.Players.ToList().ForEach(p => WriteLine($"- {p.FirstName} {p.PersonType}"));
});
WriteLine($"Players:");
session.Query<Person>().ToList()
.ForEach(c => WriteLine($"- {c.FirstName} {c.LastName} {c.PersonId} {c.Team?.TeamId} {c.PersonType}"));
}
}
ReadKey();
}
}
The one-to-one relationship between the team and the manager appears to be correct from the console output, but the one-to-many relationship between the team and the players does not work - any thoughts where my mapping classes are wrong, or how I am inserting them?
In case we use Inverse()
we must do inverse mapping even in code - i.e. set bi-directional relation on both sides.
// this is wrong, we do not provide inverse relation
var players = new List<Person>()
{
manager,
new Person { FirstName = "Mark", PersonType = PersonType.Player },
new Person { FirstName = "Karl", PersonType = PersonType.Player }
};
The above code does not provide Person with its Team;
// this is wrong, we do not provide inverse relation
var team = new Team { ... };
var p1 = new Person { FirstName = "Mark", PersonType = PersonType.Player };
var p2 = new Person { FirstName = "Karl", PersonType = PersonType.Player };
// the inverse relation is a MUST
p1.Team = team;
p2.Team = team;
// this will do the cascade
team.Players.Add(p1);
team.Players.Add(p2);
// now only team could be saved, the rest will be done by cascades
session.Save(team);