Mapping a a simple collection of elements with Fluent NHibernate
Categories:
Mapping Simple Collections 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.
.AsBag()
method specifies that the collection should be mapped as a bag, which allows duplicate elements and does not maintain order. Other options include .AsSet()
for unique elements or .AsList()
for ordered collections (which requires an index column).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.
protected
. NHibernate uses this for instantiation.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.