Fluent NHibernate Mappings: Cheat Sheet

Here is a "cheat sheet" for most of the different types of mappings I normally need to deal with in a Domain Model.

Domain Model - based on a Football Team (Australian Rules Football):

public class Team
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }

    //One to one (uni directional)
    public virtual President President { get; set; }

    //One to one (bi directional)
    public virtual Coach Coach { get; set; }

    //One to many (uni directional)
    public virtual IList<Player> Players { get; set; }

    //One to many (bi directional)
    public virtual IList<AssistantCoach> AssistantCoaches { get; set; }

    //Component
    public virtual Stadium HomeGround { get; set; }
}

public class Stadium
{
    public virtual string Name { get; set; }
    public virtual string Location { get; set; }
}

public class Player
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual int Number { get; set; }
}

public class Coach
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual DateTime AppointmentDate { get; set; }
    public virtual Team Team { get; set; }
}

public class AssistantCoach
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual CoachingLine Line  { get; set; }

    public virtual Team Team { get; set; }
}

public enum CoachingLine
{
    ForwardLine,
    Midfield,
    BackLine
}

public class President
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

Entity Mappings

public class TeamMapping : ClassMap<Team>
{
    public TeamMapping()
    {
        //Properties
        Id(entity => entity.Id);
        Map(entity => entity.Name);

        //One to many (uni-directional)
        HasMany(x => x.Players)
            .KeyColumn("TeamId")
            .Not.Inverse()    
            .Not.KeyNullable()
            .Not.KeyUpdate()
            .Cascade.All();

        //One to many (bi-directional)
        HasMany(x => x.AssistantCoaches)
            .KeyColumn("TeamId")
            .Inverse()  //This ensures the TeamId is INSERTED (rather than an INSERT with null teamid, and then an UPDATE to set the teamid)
            .Cascade.All();

        //One to one (bi-directional) - foreign key from Child -> Parent
        HasOne(x => x.Coach)
            .Cascade.All();

        //One to one (uni-directional) - foreign key from Parent -> Child
        References(x => x.President)
            .Column("PresidentId")
            .Cascade.All();

        //Component
        Component(x => x.HomeGround, m =>
        {
            m.Map(x => x.Name, "HomeGroundName");
            m.Map(x => x.Location, "HomeGroundLocation");
        });
    }
}

public class PlayerMapping : ClassMap<Player>
{
    public PlayerMapping()
    {
        Id(entity => entity.Id);
        Map(entity => entity.Name);
        Map(entity => entity.Number);
    }
}

public class CoachMapping : ClassMap<Coach>
{
    public CoachMapping()
    {
        Id(entity => entity.Id);
        Map(entity => entity.Name);
        Map(entity => entity.AppointmentDate);

        //One to one (bi directional)
        References(x => x.Team)
            .Column("TeamId");
    }
}

public class AssistantCoachMapping : ClassMap<AssistantCoach>
{
    public AssistantCoachMapping()
    {
        Id(entity => entity.Id);
        Map(entity => entity.Name);
        Map(entity => entity.Line);

        //Many to one - the other side of the One to Many (bi-directional)
        References(x => x.Team)
            .Column("TeamId");
    }
}

public class PresidentMapping : ClassMap<President>
{
    public PresidentMapping()
    {
        Id(entity => entity.Id);
        Map(entity => entity.Name);
    }
}

Test (a very simple test)

[TestClass]
public class NHibernateTests
{
    private const string ConnectionString = @"data source=(local);Integrated Security=SSPI;Database=NHibernateSample";

    [TestInitialize]
    public void Setup()
    {
        var factory = new NHibernateUnitOfWorkFactory<Team>(ConnectionString);
        factory.CreateDatabase();

        UnitOfWork.Initialise(factory);
    }

    [TestMethod]
    public void Test()
    {
        var id = 0;
        using(UnitOfWork.Create())
        {
            var team = new Team() {Name = "Melbourne Demons", Players = new List<Player>(), AssistantCoaches = new List<AssistantCoach>()};

            team.Players.Add(new Player() {Name = "Jack Watts", Number = 4});
            team.Players.Add(new Player() { Name = "Jack Trengove", Number = 9 });

            team.Coach = new Coach() { Name = "Mark Neeld", AppointmentDate = new DateTime(2011, 10, 1), Team = team};

            team.AssistantCoaches.Add(new AssistantCoach() { Name = "Jade Rawlings", Line = CoachingLine.BackLine, Team = team});
            team.AssistantCoaches.Add(new AssistantCoach() { Name = "Brian Royal", Line = CoachingLine.Midfield, Team = team });
            team.AssistantCoaches.Add(new AssistantCoach() { Name = "Leigh Brown", Line = CoachingLine.ForwardLine, Team = team });

            team.President = new President() {Name = "Jim Stynes"};

            team.HomeGround = new Stadium() {Name = "MCG", Location = "Richmond"};

            UnitOfWork.Current.DatabaseSession.Add(team);
            UnitOfWork.Commit();
            id = team.Id;
        }


        using(UnitOfWork.Create())
        {
            var loadedTeam = UnitOfWork.Current.DatabaseSession.Get<Team>(id);

            //Aggregate root
            Assert.IsNotNull(loadedTeam);

            //One to many (uni directional)
            Assert.AreEqual(2, loadedTeam.Players.Count);

            //One to many (bi directional)
            Assert.AreEqual(3, loadedTeam.AssistantCoaches.Count);

            //One to one (uni directional)
            Assert.IsNotNull(loadedTeam.President.Name);

            //One to one (bi directional)
            Assert.IsNotNull(loadedTeam.Coach.Name);

            //Component
            Assert.IsNotNull(loadedTeam.HomeGround.Name);
        }
    }
}

NOTE: The UnitOfWork and NHibernateUnitOfWorkFactory are some wrapper classes that I have developed to help abstract away the ORM details. They are available on my GitHub account.

Generated SQL:

NHibernate: INSERT INTO [President] (Name) VALUES (@p0);

NHibernate: INSERT INTO [Team] (Name, PresidentId, HomeGroundName, HomeGroundLocation) VALUES (@p0, @p1, @p2, @p3);

NHibernate: INSERT INTO [Player] (Name, Number, TeamId) VALUES (@p0, @p1, @p2);

NHibernate: INSERT INTO [Player] (Name, Number, TeamId) VALUES (@p0, @p1, @p2);

NHibernate: INSERT INTO [AssistantCoach] (Name, Line, TeamId) VALUES (@p0, @p1, @p2);

NHibernate: INSERT INTO [AssistantCoach] (Name, Line, TeamId) VALUES (@p0, @p1, @p2);

NHibernate: INSERT INTO [AssistantCoach] (Name, Line, TeamId) VALUES (@p0, @p1, @p2);

NHibernate: INSERT INTO [Coach] (Name, AppointmentDate, TeamId) VALUES (@p0, @p1, @p2);

NHibernate: SELECT team0.Id as Id41, team0.Name as Name41, team0.PresidentId as Presiden341, team0.HomeGroundName as HomeGrou441, team0.HomeGroundLocation as HomeGrou541, coach1.Id as Id10, coach1.Name as Name10, coach1.AppointmentDate as Appointm310, coach1.TeamId as TeamId10_ FROM [Team] team0_ left outer join [Coach] coach1_ on team0.Id=coach1.Id WHERE team0_.Id=@p0;@p0 = 1 [Type: Int32 (0)]

NHibernate: SELECT players0.TeamId as TeamId1, players0.Id as Id1, players0.Id as Id20, players0.Name as Name20, players0.Number as Number20_ FROM [Player] players0_ WHERE players0_.TeamId=@p0;@p0 = 1 [Type: Int32 (0)]

NHibernate: SELECT assistantc0.TeamId as TeamId1, assistantc0.Id as Id1, assistantc0.Id as Id00, assistantc0.Name as Name00, assistantc0.Line as Line00, assistantc0.TeamId as TeamId00 FROM [AssistantCoach] assistantc0_ WHERE assistantc0_.TeamId=@p0;@p0 = 1 [Type: Int32 (0)]

NHibernate: SELECT president0.Id as Id30, president0.Name as Name30 FROM [President] president0_ WHERE president0_.Id=@p0;@p0 = 1 [Type: Int32 (0)]

ormnhibernate
Posted by: Matt Callahan
Last revised: 22 Jan, 2012 04:41 AM History

Comments

No comments yet. Be the first!

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview