Mapping a a simple collection of elements with Fluent NHibernate

Learn mapping a a simple collection of elements with fluent nhibernate with practical examples, diagrams, and best practices. Covers fluent-nhibernate, nhibernate-mapping, collections development t...

Mapping Simple Collections with Fluent NHibernate

Hero image for Mapping a a simple collection of elements with Fluent NHibernate

Learn how to effectively map basic collections of value types or components using Fluent NHibernate, enhancing your data persistence layer.

Fluent NHibernate simplifies the process of mapping your domain model to a relational database. When dealing with simple collections, such as a list of strings or a collection of embedded value objects, Fluent NHibernate provides straightforward mechanisms to persist and retrieve this data. This article will guide you through the essential steps and configurations for mapping such collections, ensuring your application's data layer is robust and maintainable.

Understanding Collection Mapping Basics

In NHibernate, collections are typically mapped as separate tables linked by a foreign key. For simple collections (e.g., IList<string>, ISet<int>), NHibernate creates a dedicated table with two columns: one for the foreign key back to the parent entity, and one for the collection element's value. For collections of components, the component's properties are mapped as columns in this collection table.

erDiagram
    CUSTOMER ||--o{ ADDRESS : "has many"
    CUSTOMER { 
        int Id PK
        string Name
    }
    ADDRESS { 
        int CustomerId FK
        string Street
        string City
    }

Entity-Relationship diagram illustrating a one-to-many relationship for a collection.

Mapping a Collection of Value Types

Let's consider a Product entity that has a collection of Tags (strings). We want to store these tags in a separate table linked to the Product.

First, define your Product entity with an IList<string> property for Tags.

public class Product
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<string> Tags { get; set; }

    public Product()
    {
        Tags = new List<string>();
    }
}

The Product entity with a Tags collection.

Next, create the Fluent NHibernate mapping for the Product entity. The HasMany method is used for collections, and Element specifies that the collection contains simple value types.

public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);

        HasMany(x => x.Tags)
            .AsBag()
            .Element("Tag") // Column name for the tag value
            .Table("ProductTags") // Table to store the tags
            .KeyColumn("ProductId") // Foreign key column in ProductTags table
            .Cascade.AllDeleteOrphan();
    }
}

Fluent NHibernate mapping for the Product entity's Tags collection.

Mapping a Collection of Components

Now, let's consider a User entity that has a collection of EmailAddress components. An EmailAddress is a simple value object with a Value property.

First, define the EmailAddress component and the User entity.

public class EmailAddress
{
    public virtual string Value { get; set; }

    // NHibernate requires a parameterless constructor
    protected EmailAddress() { }

    public EmailAddress(string value)
    {
        Value = value;
    }

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
            return false;
        var other = (EmailAddress)obj;
        return Value == other.Value;
    }

    public override int GetHashCode()
    {
        return (Value != null ? Value.GetHashCode() : 0);
    }
}

public class User
{
    public virtual int Id { get; set; }
    public virtual string Username { get; set; }
    public virtual IList<EmailAddress> EmailAddresses { get; set; }

    public User()
    {
        EmailAddresses = new List<EmailAddress>();
    }
}

The EmailAddress component and User entity.

Next, create the Fluent NHibernate mappings for both the User entity and the EmailAddress component. For components, you use ComponentMap.

public class EmailAddressMap : ComponentMap<EmailAddress>
{
    public EmailAddressMap()
    {
        Map(x => x.Value).Column("Email");
    }
}

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        Map(x => x.Username);

        HasMany(x => x.EmailAddresses)
            .AsBag()
            .Component(c =>
            {
                c.Map(x => x.Value).Column("Email");
            })
            .Table("UserEmails")
            .KeyColumn("UserId")
            .Cascade.AllDeleteOrphan();
    }
}

Fluent NHibernate mappings for the User entity and EmailAddress component collection.

1. Define Your Entities and Components

Create your domain classes, including the parent entity and the value type or component class for the collection elements. Ensure component classes have a parameterless constructor.

2. Create Fluent NHibernate Mappings

For value type collections, use HasMany().Element(). For component collections, use HasMany().Component() and define the component's properties within the lambda or via a separate ComponentMap.

3. Configure Collection Properties

Specify the collection type (.AsBag(), .AsSet(), .AsList()), the table name (.Table()), the foreign key column (.KeyColumn()), and cascade options (.Cascade.AllDeleteOrphan()).

4. Build Your Session Factory

Integrate your mappings into your Fluent NHibernate configuration and build your ISessionFactory to ensure all mappings are correctly registered.