This article explains and illustrates on a practical example, how the support of different association mapping is implemented in Entity Developer with Fluent NHibernate. 

This article also contains samples of mapping for the following associations: 

One-To-Many Associations: 

NHibernate provides several ways for collection mappings: 

 - Set;  

 - Bag; 

 - List; 

 - Map(Dictionary); 

 - Array. 

Within the database, a one-to-many mapping is arranged as a table with a primary key for the one side of the mapping and another table that contains a field, which references the primary key of the first table. 

There can be a collection of items with no key that would allow retrieving them from the collection. In the collection, all elements can be defined as unique (a Set) or, alternatively, they can be duplicated (a Bag). 

It is also possible to arrange a collection of items with a key that allows retrieving those items from the collection. This key can be either an integer (a List) or another simple type, or even an object (in this case, it is called a Dictionary). 

Set and Map Types of Collection Mapping Sample: 

The database contains the Dept and Emp tables, which have a foreign key constraint between them. 

The field DEPTNO in the table Emp receives the id value of the associated record in the Dept table. 

 1.png

We perform the following sequence of operations: create an NHibernate model, add the Dept and Emp tables to the model, add the additional Fluent NHibernate template to generate fluent mapping. In the result we have the following model: 

2.png

If the navigation property Emps is defined as Set, then the generated fluent mapping for the model will be as follows: 

    public class EmpMap : ClassMap<Emp>
    {
        public EmpMap()
        {
              Schema("dbo");
              Table("Emp");
              LazyLoad();
              Id(x => x.EMPNO)
                .Column("EMPNO")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.ENAME)
                .Column("ENAME")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(10);
              Map(x => x.JOB)
                .Column("JOB")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(9);
              Map(x => x.MGR)
                .Column("MGR")
                .CustomType("Int32")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("int")
                .Precision(10);
              Map(x => x.HIREDATE)
                .Column("HIREDATE")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("datetime");
              Map(x => x.SAL)
                .Column("SAL")
                .CustomType("Double")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("float")
                .Precision(53);
              Map(x => x.COMM)
                .Column("COMM")
                .CustomType("Double")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("float")
                .Precision(53);
              References(x => x.Dept)
                .Class<Dept>()
                .Access.Property()
                .Cascade.None()
                .LazyLoad()
                .Columns("DEPTNO");
        }
    }
    public class DeptMap : ClassMap<Dept>
    {
        public DeptMap()
        {
              Schema("dbo");
              Table("Dept");
              LazyLoad();
              Id(x => x.DEPTNO)
                .Column("DEPTNO")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.DNAME)
                .Column("DNAME")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(14);
              Map(x => x.LOC)
                .Column("LOC")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(13);
              HasMany<Emp>(x => x.Emps)
                .Access.Property()
                // for the Bag collection mapping, we need to call AsBag() 
                .AsSet()  // for the Set collection mapping, we call AsSet()
                .Cascade.None()
                .LazyLoad()
                .Inverse()
                .KeyColumns.Add("DEPTNO", mapping => mapping.Name("DEPTNO")
                                                            .SqlType("int")
                                                            .Nullable());
        }
    } 

The code above represents the generated model mapping classes. The mapping of each class  contains the descriptions of the identity key mapping and class properties, as well as the definitions of the name of the table in the database, the name of the schema, to which the table belongs. In the mapping of the Emp class the References function is used to create a reference to the master entity Dept. In database terms, this is a many-to-one relationship; while the Columns function specifies the name of the details table(Emp) column, against which the foreign key is created in the database together with the master table(Dept). In the mapping of the Dept class the HasMany function is used to map a collection of entities as a one-to-many association, the call of AsSet() indicates that the collection is mapped as a Set; while the KeyColumns.Add function specifies the name of the details table(Emp) column, against which the foreign key is created in the database together with the master table(Dept), its type and other mapping parameters.  

On the other hand, if the collection type of the navigation property is set to Bag, than the generated fluent mapping for this model will be the same as shown above, except that when the HasMany function  is used to map the collection of entities as a one-to-many, the AsBag() function will be called instead of the AsSet() function to indicate that the collection is mapped as a Bag. All the other aspects of the mapping will be identical.  

Map(Dictionary) type of collection mapping sample:

In this example, we shall use the tables from the previous example for Set and Bag collection mapping.

The model is created in the same way as in the previous example. After the model has been created, we need to display the Association Editor dialog. Through this dialog box, we need to set the Generate relation property of the Dept navigation property to False, since only Set or Bag may be used for the “many” end of a bidirectional association. After that, we need to set the collection type of the Emps navigation property to Map and specify the ENAME column as the Index column. Now the Employees navigation property of the class Dept is mapped as Map collection mapping and is filled with instances of the class Emp. You can find the instances by looking in the mapped table for Emp and find records with the field DEPTNO having value equal to DEPTNO field value of the table Dept. The field ENAME in the table Emp receives the key value of the entry in the dictionary. The key used in the map is of type String and can be found in the column ENAME of the table used to store the Emp objects. The type of the key used in this sample is string.  

Below is the generated fluent mapping for this model: 

    public class DeptMap : ClassMap<Dept>
    {
        public DeptMap()
        {
              Schema("dbo");
              Table("Dept");
              LazyLoad();
              Id(x => x.DEPTNO)
                .Column("DEPTNO")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.DNAME)
                .Column("DNAME")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(14);
              Map(x => x.LOC)
                .Column("LOC")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(13);
              HasMany<Emp>(x => x.Emps)
                .Access.Property()
                .AsMap<string>("ENAME")
                .Cascade.None()
                .LazyLoad()
                .KeyColumns.Add("DEPTNO", mapping => mapping.Name("DEPTNO")
                                                            .SqlType("int")
                                                            .Nullable());
        }
    }
    public class EmpMap : ClassMap<Emp>
    {
        public EmpMap()
        {
              Schema("dbo");
              Table("Emp");
              LazyLoad();
              Id(x => x.EMPNO)
                .Column("EMPNO")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.ENAME)
                .Column("ENAME")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(10);
              Map(x => x.JOB)
                .Column("JOB")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(9);
              Map(x => x.MGR)
                .Column("MGR")
                .CustomType("Int32")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("int")
                .Precision(10);
              Map(x => x.HIREDATE)
                .Column("HIREDATE")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("datetime");
              Map(x => x.SAL)
                .Column("SAL")
                .CustomType("Double")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("float")
                .Precision(53);
              Map(x => x.COMM)
                .Column("COMM")
                .CustomType("Double")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("float")
                .Precision(53);
        }
    } 

The code above represents the generated model mapping classes. The mapping of each class contains the descriptions of the identity key mapping and class properties, as well as the definitions of the name of the table in the database, the name of the schema, to which the table belongs. In the mapping of the Dept class the HasMany function is used to map a collection of entities as a one-to-many, the call of AsMap<string>("ENAME") indicates that the collection is mapped as Map and specifies the key type and the name of the column that  receives the key value of the entry in the dictionary; while the KeyColumns.Add function specifies the name of the details table(Emp) column, against which the foreign key is created in the database together with the master table(Dept), as well as its type and other mapping parameters. 

List and Array types of collection mapping sample: 

The database contains the Message and Conversation tables, linked though the foreign key constraint.

The field ConversationId in the Message table receives the id value of the associated record in the Conversation table. Additionally, the Message table has the Order column, whose value determines the sequence of the message in the message list of each conversation. 

3.png

We perform the following sequence of operations: create an NHibernate model, add the Message and Conversation tables to the model; display the Association Editor dialog, through which we set the Generation relation property of the Conversation property to False, since only Set or Bag may be used for the “many” end of a bidirectional association. After that, we set the collection type of the Messages navigation property to List or Array, specify the Order column as the Index column, add the additional Fluent NHibernate template to generate fluent mapping. In the result, we have the following model: 

4.png

If the collection type of the Emps navigation property is set to List, then the generated fluent mapping for this model will be as follows: 

    public class ConversationMap : ClassMap<Conversation>
    {
        public ConversationMap()
        {
              Schema("NHListSample");
              Table("Conversation");
              LazyLoad();
              Id(x => x.Id)
                .Column("Id")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.Description)
                .Column("Description")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("varchar")
                .Length(255);
              Map(x => x.Date)
                .Column("DATE")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .Default("getdate()")
                .CustomSqlType("datetime")
                .Not.Nullable();
              HasMany<Message>(x => x.Messages)
                .Access.Property()
                // for the List collection mapping, we need to call AsList
                .AsList(index => index.Column("`Order`").Type<int>())
                // for the Array collection mapping, we need to call AsArray
                // .AsArray<int>(child => child.Id, map =>   map.Column("`Order`").Type<int>())
                .Cascade.None()
                .LazyLoad() // for the Array collection mapping we need to remove
                .KeyColumns.Add("ConversationId", mapping => mapping.Name("ConversationId")
                                                            .SqlType("int")
                                                            .Not.Nullable());
        }
    }
    public class MessageMap : ClassMap<Message>
    {
        public MessageMap()
        {
              Schema("NHListSample");
              Table("Message");
              LazyLoad();
              Id(x => x.Id)
                .Column("Id")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)                
                .GeneratedBy.Identity();
              Map(x => x.Order)
                .Column("`Order`")
                .CustomType("Int32")
                .Access.Property()
                .Generated.Never()
                .Default("'0'")
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10);
              Map(x => x.Text)
                .Column("Text")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .Default("''")
                .CustomSqlType("varchar")
                .Not.Nullable()
                .Length(45);
        }
    } 

The code above represents the generated model mapping classes. The mapping of each class  contains the descriptions of the identity key mapping and class properties, as well as the definitions of the name of the table in the database, the name of the schema, to which the table belongs. In the mapping of the Conversation class, the HasMany function is used to map a collection of entities as a one-to-many association, and the call of AsList() indicates that the collection is mapped as List; here we also specify mapping for the index; while the KeyColumns.Add function specifies the name of the details table(Message) column, against which the foreign key is created in the database together with the master table(Conversation), its type and other mapping parameters. The index (and thus position) of the element in the list is specified by the value in the Order field of the table. 

Alternatively, if the collection type of the Emps navigation property is set to Array, then the genrated fluent mapping for this model will be the same as above, however, in the mapping of the Conversation class, when the HasMany function is used to map a collection of entities as a one-to-many association, instead of calling AsList(index => index.Column("`Order`").Type<int>()) we have to call AsArray<int>(child => child.Id, map => map.Column("`Order`").Type<int>()) that sets the Array collection mapping. It should also be noted that we cannot call LazyLoad() for Array collection mapping, so we need to remove this call. 

Many-To-Many Associations: 

Many-to-many associations use an intermediate table that has a foreign key to the tables of both associated entities. An object uses several objects of another type, and this latter object references several objects of the first type. As is always the case for many-to-many mappings in relational databases, we need a third table which provides the cross-references for the many-to-many relationship.  

The database contains the Employees, Territories tables and EmployeeTerritories table, which provides the cross-references for the many-to-many relationship. 

5.png

We perform the following sequence of operations: create an NHibernate model, add the  Employees, Territories, and EmployeeTerritories tables, add the additional Fluent NHibernate template to generate fluent mapping. In the result, we have the following model: 

6.png

The Territories navigation property of the type Employees is a many-to-many property to a Set collection of objects with the type Territory. The values for the objects in this collection can be found by looking for entries in the table for Territory objects with an TerritoryID equal to the values in the column TerritoryID of the EmployeeTerritories table with a value of EmployeeID equal to this object's EmployeeID.

The Employees navigation property of the type Territory is a many-to-many property to a Set collection of objects with the type Employees. The values for the objects in this collection can be found by looking for entries in the table for Employees objects with an EmployeeID equal to the values in the column EmployeeID of the EmployeeTerritories table with a value of TerritoryID equal to this object's TerritoryID.

In our example, both navigation properties are mapped as a Set, although for many-to-many associations we can use any other type of collection mapping, if needed.

Below is the generated fluent mapping for this model:  

    public class TerritoryMap : ClassMap<Territory>
    {
        public TerritoryMap()
        {
              Schema("dbo");
              Table("Territories");
              LazyLoad();
              Id(x => x.TerritoryID)
                .Column("TerritoryID")
                .CustomType("String")
                .Access.Property()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(20)
                .GeneratedBy.Assigned();
              Map(x => x.TerritoryDescription)
                .Column("TerritoryDescription")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nchar")
                .Not.Nullable()
                .Length(50);
              Map(x => x.RegionID)
                .Column("RegionID")
                .CustomType("Int32")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10);
              HasManyToMany<Employees>(x => x.Employees)
                .Access.Property()
                .AsSet()
                .Cascade.None()
                .LazyLoad()
                .Inverse()
                .Schema("dbo")
                .Table("EmployeeTerritories")
                .FetchType.Join()
                .ChildKeyColumns.Add("EmployeeID", mapping => mapping.Name("EmployeeID")
                                                                     .SqlType("int")
                                                                     .Not.Nullable())
                .ParentKeyColumns.Add("TerritoryID", mapping => mapping.Name("TerritoryID")
                                                                     .SqlType("nvarchar")
                                                                     .Not.Nullable()
                                                                     .Length(20));
        }
    }
    public class EmployeesMap : ClassMap<Employees>
    {
        public EmployeesMap()
        {
              Schema("dbo");
              Table("Employees");
              LazyLoad();
              Id(x => x.EmployeeID)
                .Column("EmployeeID")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)                
                .GeneratedBy.Identity();
              Map(x => x.LastName)
                .Column("LastName")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(20);
              Map(x => x.FirstName)
                .Column("FirstName")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(10);
              Map(x => x.Title)
                .Column("Title")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(30);
              Map(x => x.TitleOfCourtesy)
                .Column("TitleOfCourtesy")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(25);
              Map(x => x.BirthDate)
                .Column("BirthDate")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("datetime");
              Map(x => x.HireDate)
                .Column("HireDate")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("datetime");
              Map(x => x.Address)
                .Column("Address")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(60);
              Map(x => x.City)
                .Column("City")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(15);
              Map(x => x.Region)
                .Column("Region")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(15);
              Map(x => x.PostalCode)
                .Column("PostalCode")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(10);
              Map(x => x.Country)
                .Column("Country")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(15);
              Map(x => x.HomePhone)
                .Column("HomePhone")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(24);
              Map(x => x.Extension)
                .Column("Extension")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(4);
              Map(x => x.Photo)
                .Column("Photo")
                .CustomType("BinaryBlob")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("image")
                .Length(2147483647);
              Map(x => x.Notes)
                .Column("Notes")
                .CustomType("StringClob")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("ntext")
                .Length(1073741823);
              Map(x => x.ReportsTo)
                .Column("ReportsTo")
                .CustomType("Int32")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("int")
                .Precision(10);
              Map(x => x.PhotoPath)
                .Column("PhotoPath")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(255);
              HasManyToMany<Territory>(x => x.Territories)
                .Access.Property()
                .AsSet()
                .Cascade.None()
                .LazyLoad()
                .Schema("dbo")
                .Table("EmployeeTerritories")
                .FetchType.Join()
                .ChildKeyColumns.Add("TerritoryID", mapping => mapping.Name("TerritoryID")
                                                                     .SqlType("nvarchar")
                                                                     .Not.Nullable()
                                                                     .Length(20))
                .ParentKeyColumns.Add("EmployeeID", mapping => mapping.Name("EmployeeID")
                                                                     .SqlType("int")
                                                                     .Not.Nullable());
        }
    } 

The code above represents the generated model mapping classes. The mapping of each class contains the the descriptions of the identity key mapping and class properties, as well as the definitions of the name of the table in the database, the name of the schema, to which the table belongs. Additionally, in the mapping of the classes, the HasManyToMany function is used to set up the mapping for the navigation property of the many-to-many association: we need to specify the type of collection mapping, the name of the link table in the database, which provides the cross-references for the many-to-many relationship, the name of the schema, to which the table belongs; we also need to specify the mapping for columns of the (EmployeeTerritories) link table, which will provide cross-references for the many-to-many relationship between the associated tables (Employees and Territories).

One-To-One Associations:

One-to-one associations are the simplest kind of associations. They usually correspond to the foreign key relations when the foreign key column is the primary key column. 

The following two cases are possible, when we map a one-to-one association:

Case 1.  When the foreign key and the primary key in the dependent entity class (class, corresponding to the table that has the foreign key) consist of the same columns. 

The database contains the Person and Contact tables, between which there is a foreign key created against the primary key fields of these tables.  

7.png 

We perform the following sequence of operations: create an NHibernate model, add the Person and Contact tables to the model, add the additional Fluent NHibernate template to generate fluent mapping. In the result we have the following model:

8.png 

Below is the generated fluent mapping for this model: 

    public class ContactMap : ClassMap<Contact>
    {
        public ContactMap()
        {
              Schema("dbo");
              Table("Contact");
              LazyLoad();
              Id(x => x.ContactID)
                .Column("ContactID")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.FirstName)
                .Column("FirstName")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(50);
              Map(x => x.LastName)
                .Column("LastName")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(50);
              Map(x => x.Email)
                .Column("Email")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(50);
              Map(x => x.Phone)
                .Column("Phone")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nchar")
                .Length(13);
              HasOne(x => x.Person)
                .Class<Person>()
                .Access.Property()
                .Cascade.None()
                .LazyLoad();
        }
    }
    public class PersonMap : ClassMap<Person>
    {
        public PersonMap()
        {
              Schema("dbo");
              Table("Person");
              LazyLoad();
              Id(x => x.PersonID)
                .Column("PersonID")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.BirthDay)
                .Column("BirthDay")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("datetime")
                .Not.Nullable();
              Map(x => x.ADDRESS)
                .Column("ADDRESS")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(200);
              HasOne(x => x.Contact)
                .Class<Contact>()
                .Access.Property()
                .Cascade.None()
                .LazyLoad()
               // indicates that the (Person) table that corresponds to the type has a foreign key
                .Constrained();
        }
    } 

The code above represents the generated model mapping classes. The mapping of each class  contains the descriptions of the identity key mapping and class properties, as well as the definitions of the name of the table in the database, the name of the schema, to which the table belongs. Additionally, in the mapping of the classes the HasOne function is used to set up the mapping of the navigation property of the one-to-one association: it specifies the class, which the navigation property refers to, its access and other mapping settings, while the call of the Constrained() in this definition indicates that the (Person) table, corresponding to this type, has the foreign key. 

Case 2.  When foreign key is some non-primary unique property. You need to specify the foreign key column of the constrained table in this case. 

The database contains the Person and Contact tables, between which there is a foreign key that is created against the ContactID  primary key field of the Contact table and the ContactID unique field of the Person table. 

9.png

We perform the following sequence of operations: create an NHibernate model, add the Person and Contact tables to the model; display the Association Editor dialog and set the Separate Fioreign Key parameter of the Contact property to True as well as set the Foreign Key column to the ContactID column, and, finally, add the additional Fluent NHibernate template to generate fluent mapping. In the result we have the following model: 

10.png

Below is the generated fluent mapping for this model: 

    public class ContactMap : ClassMap<Contact>
    {
        public ContactMap()
        {
              Schema("OneToOneSeparateFK");
              Table("Contact");
              LazyLoad();
              Id(x => x.ContactID)
                .Column("ContactID")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.FirstName)
                .Column("FirstName")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(50);
              Map(x => x.LastName)
                .Column("LastName")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(50);
              Map(x => x.Email)
                .Column("Email")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Length(50);
              Map(x => x.Phone)
                .Column("Phone")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nchar")
                .Length(13);
              HasOne(x => x.Person)
                .Class<Person>()
                .Access.Property()
                .Cascade.None()
                .LazyLoad()
                // Sets the property reference
                .PropertyRef(p => p.Contact);
        }
    }
    public class PersonMap : ClassMap<Person>
    {
        public PersonMap()
        {
              Schema("OneToOneSeparateFK");
              Table("Person");
              LazyLoad();
              Id(x => x.PersonID)
                .Column("PersonID")
                .CustomType("Int32")
                .Access.Property()
                .CustomSqlType("int")
                .Not.Nullable()
                .Precision(10)
                .GeneratedBy.Assigned();
              Map(x => x.BirthDay)
                .Column("BirthDay")
                .CustomType("DateTime")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("datetime")
                .Not.Nullable();
              Map(x => x.ADDRESS)
                .Column("ADDRESS")
                .CustomType("String")
                .Access.Property()
                .Generated.Never()
                .CustomSqlType("nvarchar")
                .Not.Nullable()
                .Length(200);
              References(x => x.Contact)
                .Class<Contact>()
                .Access.Property()
                .Cascade.None()
                .LazyLoad()
                // specifies the column used in this relationship
                .Columns("ContactID");
        }
    } 

The code above represents the generated model mapping classes. The mapping of each class  contains the descriptions of the identity key mapping and class properties, as well as the definitions of the name of the table in the database, the name of the schema, to which the table belongs. In the mapping of the Contact class, the HasOne function is used to set up mapping of the Person navigation property of the one-to-one association: the function specifies the class the navigation property references, navigation property access, and other settings of the mapping. In the mapping of the Person class, the References function is used to set up the mapping of the Contact navigation property of the one-to-one association: it specifies the class the navigation property references, specifies the ContactID field of the Person table, against which the foreign key with the Contact table is created, navigation property access and other settings of the mapping.  

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"