Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

Object Relational Mapping (ORM) using NHibernate - Part 1 of 8 Coding One to One Entity Associations

Rate me:
Please Sign up or sign in to vote.
4.96/5 (27 votes)
27 Jan 2013CPOL21 min read 142.1K   127   15
A full series of 8 part articles to show One-To-One, Many-To-One, Many-To-Many associations mapping using NHibernate, Using Collections With NHibernate, Inheritance Relationships Using NHibernate, Lazy Initializations/Fetches Using NHibernate.

PART 2

PART 3

PART 4

PART 5 A

PART 5 B

PART 6

PART 7

PART 8

Introduction

For object relational mapping in a rapid application development setup using c#, the default choice is Language Integrated Query and the other main option is NHibernate. There are scenarios where NHibernate is an useful addition. Both SPRING.NET and CASTLE WINDSOR, two of the oldest DI containers, have extensive support for NHibernate. There is zero leakage of concerns when NHibernate is used correctly for persistence. If coded correctly all POCOs (PLAIN OLD CLR OBJECTS) are guaranteed to be free from persistence and transaction concerns. This adds huge advantages for domain classes reuse, unit testing and test driven development. For strategic thinking resource managers and pocos inclined architects, NHibernate offers other unique advantages they are very well aware of and needs no introductions from me. That said, unless there is a strategic or systems requirements demanding NHibernate usage, Language Integrated Query should be the chosen path in c#. But if a requirement for NHibernate exists and you are new to ORM and NHibernate, then this 8 part article series may be useful to you.

Background

Before embarking on reading this article series on NHibernate, please read this tutorial in NHibernate page

where you will download NHibernate:

http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx

The above mentioned tutorial is OFFICIAL AND THE BEST INTRODUCTION TO SETTING UP NHibernate in VISUAL STUDIO and to jumpstart to NHibernate. TO READ MY ARTICLE SERIES, IT IS AN ABSOLUTE PREREQUISITE THAT YOU FOLLOW THE LINK ABOVE AND SETUP THE PROJECT IN VISUAL STUDIO ACCORDING TO GUIDELINES GIVEN IN IT FOR NHIBERNATE USE. The change in the visual studio setup between the article link above and sample project available for download in Article 7 is that here we use SQLSERVER DB instead of SQL SERVER Compact Edition and choose "SERVICE BASED DATABASE" option while adding the database.

+ MICROSOFT VISUAL STUDIO 2012 PROFESSIONAL TRIAL VERSION HAS BEEN USED FOR ALL SAMPLES HERE.

DOWNLOADABLE CODE SAMPLE:

Article 7 contains a downloadable console based project of the ECommerce application scenario (simple pocos, just to give the idea of association in an easy to understand way). Examples of all articles are based in this ECommerce scenario. In articles 1 - 5, we will work towards evolving the various object associations for the full sample of Article 7. Since its console based, it will have only pocos. In Articles 1-5, I have embedded the NHibernate and C# code for establishing the associations from the sample project of Article 7 in the article itself with explanations and figures, to show how to map the various associations in Nhibernate. Read through Article 1- 5 as they have the code samples embeded in figures in the article with explanations.

Using the code And Using NHibernate For One-To-One Mapping

Hopefully you have read the article given in Pre-Requisites section posted on the official NHibernate site blogs and tried it because its the best introduction to NHibernate.

Fundamental starting point in developing / consuming ORM software is fixing the identity of object. Object Identity is important for all ORM systems including NHibernate because when a CLR loaded object is modified, an ORM software must know without ambiguity to which particular row in the database table the changes must be made persistent by using DML sql statements which are generated automatically for every unit of action performed in the object (object creation & save=insert statement, object modification = update statement, object deletion = delete statement). What this means in plain words is that ORM software should know, to what row in a database table the object maps to without ambiguity (Though this statement is more applicable to object-relation mappings using Row Gateway pattern, we will not get into that discussion here.). So every object will have a separate identity assigned to it, which is stored as a property in it and mapped in NHibernate mapping file using the keyword <id>. Astute developers would immediately jump to the conclusion that this identifier must be related to PRIMARY KEY because i said "unambiguously identify a single row in a database table". Yes you are right and this identifier property in every class is essentially its primary key in the database table and it is best to keep it as a synthetic or surrogate key.

Please refer to the example FIGURE - I below which shows a C# class called Customer having a property called CustomerId of type long mapped using the NHibernate mapping file - Customer.hbm to a Database Table called CUSTOMER TABLE with the same CustomerId property of C# class becoming a primary key of the table .

The GREEN ARROW SHOWS THE MAPPING OF C# CLASS TO .HBM MAPPING FILE AND HOW THE IDENTIFIER PROPERTY IN C# CLASS IS DEFINED IN .HBM MAPPING FILE USING THE <ID> TAG TO DENOTE THAT ITS NOT A NORMAL PROPERTY BUT AN OBJECT IDENTIFIER IN C# WORLD AND WILL SERVE AS A PRIMARY KEY FOR A TABLE IN DATABASE WORLD.

THE ORANGE ARROW INDICATES HOW THE TABLE IS FORMED WITH A PRIMARY KEY SET EXACTLY AS

INDICATED BY THE <ID> TAG OF NHIBERNATE MAPPING FILE. HERE IN THIS EXAMPLE IT IS "CUSTOMERID".

NImage 1

FIGURE - I

As said earlier, Fixing the object identity and mapping it to be the primary key of db table is the firs step in any ORM. So now the question is "Do we have to add the Id property to all classes by default?" . To know the answer for this, one must know the distinction between Entity and Valuetypes in NHibernate.

Entity and Value Types

The distinction between Entity and Valuetypes is well understood by an example. Consider the example of an ECommerce site which we use for all articles here. Naturally every ecommerce site stores the information about the buyer and hence has a Customer class which may include a field for EMailAddress. Hence the Customer class will need to include in its behaviour a constraint check for valid emailaddress entry and additional methods to send automated mails (there are always a plenty of these mails sent by ecommerce sites to customers). As an outcome of all this behavioural additions caused by EMailAddress field, Customer object loses cohesion. Hence EMailAddress field must be made a separate class with all this behaviour moved to EMail class. But we cannot represent EMail as a separate database table because a one to one link exists between Customer and Email and a one to one link definitely throws a hint to a modeler that both tables may need to be unified to a more appropriate entity to which a database table has to be created. Also our EMail class includes a lot of behaviour (functionality to check email address format, send email and perhaps even use email address for login id) but still has only one field - emailaddress further confirming that this one to one link needs to be unified into one entity. Alo while EMail class improves cohesion in object model,it is found that an EMail object cannot exist independently without a Customer object and always gets associated with one Customer object without SHARED REFERENCES because neither can a email be shared between two customers nor there is any use in email of a customer if the customer ceases to exist. Objects like Customer which have independent lifetime are termed Entity and dependent objects like EMail which cannot exist independently and have no shared references (strictly one to one associations only) are termed Valuetypes in NHibernate.

Entity objects have db tables and hence require identifier property in class and corresponding id attribute when they are mapped.

Valuetypes do not have a separate table and are placed in the same table as the entity to which they are tightly related in ONE to ONE relationship like a Customer entity for the Email valuetype. Hence valuetypes do not need a seperate identifier field as they are identified by the entity with which they are linked and do not have separate database table.

Since the lifecycle of Valuetype (ex. EMail) is fully dependent on its related Entity (ex. Customer) without shared references, in UML models the association between Entity and Valuetype is COMPOSITION and not AGGREGATION. This association difference in the model may not change the code for Java and C# but in C++ this difference has a huge change in the code because it means objects will have to be instantinated by value without pointers or references for Valuetypes.

LASTLY, IN NHIBERNATE, WHILE ALL ENTITY CLASSES HAVE A SEPARATE .HBM MAPPING FILE, VALUETYPES DO NOT HAVE A SEPARATE MAPPING FILE OR DB TABLE. THEY ARE PLACED WITH A <COMPONENT> TAG ON THE SAME HBM MAPPING File OF THE CLASS WITH WHICH THEY HAVE A ONE TO ONE ASSOCIATION. SO IN OUR EXAMPLE, CUSTOMER AND EMAIL CLASSES WILL HAVE THE SAME MAPPING FILE i.e CUSTOMER.HBM MAPPING FILE AND EMAIL WILL BE PLACED AS A <component> in that MAPPING FILE. WE WILL SEE HOW THIS IS DONE QUITE CLEARLY IN "EXAMPLE FOR ENTITY AND VALUETYPES" SECTION NEXT.

Identifying Entity and Valuetypes

To identify an Entity see if it satisfies the following checklist.

- Does the objects of this class have independent lifetime? Yes for entity.

- Does the objects of this class have its refrences shared? May be Yes for entity.

- Does objects of this class need a separate Database Table and identifier? Yes.

To identify a Value Type it is A DEFINITVE NO for all the questions in the check list.

Example for Entity and Valuetypes

In our sample ecommerce scenario application, there is a Customer c# class (with id property) and Email c# class (without id property) mapped to one and the same table "CUSTOMER" in the mapping file Customer.hbm.xml. We write the C# code for Customer and Email class as always and create a mapping file for Customer class by adding a Customer.hbm.xml file in Visual Studio. The Customer.hbm mapping is as below,

XML
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly ="EShopSystem" namespace ="EShopSystem.Domain" >
<class name="Customer" table="CUSTOMER" >
<id name ="CustomerId" column ="CUSTOMERID" type ="long" generator="native" />
<property name="CustomerName" column="CUSTOMERNAME" type="string" />
<component class="Email" name="EmailIdentity">
<property name="EmailAddress" column="EMAILADDRESS" type="string" />
</component>
</class>
</hibernate-mapping>

Please refer to the FIGURE II below. In this figure, I show how the above mapping file corresponds to c# classes. I have put the c# class codes of Customer and Email classes on top row of the figure, side by side, separated by a black bar. Below the c# code for Customer and Email class, I have put the Customer.hbm mapping file code. By using arrows i show how the mapping is done between c# code classes and their corresponding .hbm mapping files. People who do not like this style of representation may just download the sample project in Article 7 of the series and refer to the c# code and mapping files. But this is far simple approach to understand NHibernate mappings.

Now to the explanation of the simple code in figure II below. First before proceeding further check the C# code in the figure II below and observe that the Customer Entity class has a property of type long called CustomerId to store the object identifier which is absent in the valuetype Email because as already explained earlier, the entity customer needs the identifier since it has its own database table - CUSTOMER TABLE and every row in it corresponds to a c# object of type Customer and the primary key of each row is stored in the identifier property in its corresponding c# object. Valuetype like Email do not have their own DB Table and hence do not have this identifier. Now lets proceed with the explanation of the figure II below,

The Customer class being an entity, must have a table - CUSTOMER and a corresponding Customer.hbm mapping file. Follow the Green arrow for the mapping of Customer c# class to its corresponding <class> tag in Customer.hbm mapping file.

As explained prior, Email class is a valuetype. So it cannot have a table or a mapping file of its own. So EMail is mapped in the Customer class .hbm mapping file itself by using a <component> tag as shown below in the figure II. Follow the Orange arrow from Email class c# code to its corresponding <component> tag in the same Customer.hbm mapping file.

The association between customer and email is captured in c# by usual way of storing a reference of EMail object in Customer named as EmailIdentity property in Customer C# class. Follow the purple arrow in the figure II to See the mapping of property EmailIdentity on Customer class to its corresponding tag in the mapping file.

The end result of this <component> mapping for Email class in Customer class mapping file is that in db CUSTOMER table will now carry columns for fields of Email class also. Lastly the figure II also shows the snapshot of Database Table CUSTOMER which includes the fields of Email Valuetype also (shown with a triangle in figure II).

Image 2

FIGURE II

So more classes than tables can be used which makes it possible to have a rich domain model mapped to fewer tables by mapping the classes as entity and valuetypes appropriately using the checklist given previously.

One to One Entity Association

Earlier it was found that there is a One to One association between Customer and EMail but when we analysed this One to One association more thoroughly it was found that EMail is a value type to be put in same table as Customer and hence it is not a One To One entity Association mapped to separate tables. Now the question is how to map One to One Association of Entity types.

Associations are created between objects by storing references. They may be unidirectional or bidirectional. For example, to create an association between object B & object A, we store the reference of B in A as shown in part 1 of figure III below. But Databases does not have the concept of references. Hence in Databases to establish a link from Table A to Table B, the primary key of Table B is made as Foreign Key in Table A. Now navigability is established between them. See part 2 of figure III. All ORM software maps one to one association to DB Tables by using foreign key from one table to another. While this is easily deducible for one and all, let us not forget that this detail has to be put in mapping files of the entities involved in association for NHibernate to manage this. Two separate mapping files have to be provided corresponding to each of the class involved in the association.

Image 3

FIGURE - III

Coding One to One Mapping In NHibernate:

Consider the scenario of an ECommerce application.

"The customer submits his order and makes a payment. The ecommerce application processes the order and once payment is approved, adds the order to payment approved orders. "

The association between PaymentApprovedOrder and Payment is one to one entity association in this scenaio. Without ploughing into the details of the fields and behaviour of classes PaymentApprovedOrder and Payment, let us look at the One to One association between these two entities which is more relevant to the discussion here.

Before going into the details of association, its noteworthy to understand why Payment class must be Entity and not ValueType because it looks like its lifecycle is tied to the Order class and without an Order instance a Payment cannot exist. While it is true that a Payment cannot exist without an order, the life of Payment instance extends more than an Order instance because an order is fulfilled upon shipment but a Payment object may live beyond a Order and Shipment instance especially if it takes further processing time for its fulfilment. A Payment for an Order could even extend for years if paid in parts. If you make Payment a Valuetype, then its lifetime will be over with an instance of an Order and cannot be having persistence independently without an Order instance which is a problem. Hence Payment has to be made an entity to give it independent lifetime. Just refer to the checklist for Entity and Valuetypes if still in doubt. NHibernate is all about making logical design decisions with strong object oriented and relational analysis.

The figure IV below shows how the association between Payment and PaymentApprovedOrder can be mapped. Both for Payment and PaymentApprovedOrder class, we add minimum properties as our subject of interest here is the bidirectional one to one association between them and how it is mapped in NHibernate.

In the figure IV below, the C# code for PaymentApprovedOrder and Payment is first written. Below the C# code, the .HBM Mapping files are written. Firstly to represent the association between PaymentApprovedOrder and Payment in c# code, references are stored as usual. See the c# code in figure for PaymentApprovedOrder and Payment. These references will have to be mapped in .HBM file to map the association in NHibernate. Now lets proceed to the explaination of the figure IV below to see how this association is mapped.

Let us first see how PaymentApprovedOrder is mapped. The class PaymentApprovedOrder stores a reference of Payment class instance named as OrderPayment. Look at the Orange arrow in figure IV to see how the stored Payment reference is mapped from c# class to its .HBM file.

This association is mapped as below,

In PaymentApprovedOrder. cs having the PaymentApprovedOrder c# class,

public virtual Payment OrderPayment { get; set; }

In PaymentApprovedOrder.hbm file,

<many-to-one name ="OrderPayment" lazy="false" class ="Payment" column="PAYMENTID" unique="true" not-null="true" cascade="save-update"/>

The notable thing here is we said PaymentApprovedOrder has a one to one association with Payment class. WHY IS IT MARKED WITH <MANY TO ONE> and not <ONE TO ONE>? Let us research this <many-to-one> tag first.

By this <many-to-one> tag here we specify that MANY-TO-ONE association exists between MANY instances of PaymentApprovedOrder to ONE instance of PAYMENT.

THIS USAGE SEEMS LIKE A CLEAR VIOLATION OF ONE TO ONE ASSOCIATION THAT IS REQUIRED BETWEEN PAYMENTAPPROVEDORDER AND PAYMENT.

As we already explained, an association between instances is represented in database by a foreignkey relationship. So for mapping this <many-to-one> relationship between many instances of PaymentApprovedOrder to one Payment instance, to that in the database between PAYMENTAPPROVEDORDER table and PAYMENT table, the same foreign key of PAYMENT table must be repeated for many rows of PAYMENTAPPROVEDORDER table (because its <many-to -one> and we cannot store collections in column, so we can only repeat multiple times).

This is not what we want. We want each and every row in PAYMENTAPPROVEDORDER table that represent a separate object of PaymentApprovedOrder class to be posted with a foreign key from PAYMENT Table that represents exactly one instance of Payment object SUCH THAT EACH OF THE FOREIGN KEY FROM PAYMENT TABLE OCCURS ONLY ONCE IN PAYMENTAPPROVEDORDER TABLE (because it is <one-to-one>).

If you look at the attributes for this <many-to-one> it says UNIQUE=TRUE. WHAT THIS MEANS IS THAT THE FOREIGNKEY WHICH IS GOING TO BE POSTED FROM PAYMENT TABLE TO PAYMENT APPROVEDORDER TABLE WILL BE UNIQUE FOR THAT COLUMN WITHOUT REPEATS. BECAUSE OF THIS UNIQUE ATTRIBUTE, A FOREIGN KEY POSTED FROM PAYMENT table WILL OCCUR ONLY ONCE IN PAYMENTAPPROVEDORDER table WITHOUT REPEATS thereby satisfying ONE TO ONE ASSOCIATION.

Now the question in all the minds reading this would be, Why not directly put a <one-to-one>? A <one-to-one> association has a different purpose which we will see next.

The other question would be, Are we not achieving the functionality of <one-to-one> association crudely by using <many-to-one> and constraining it with a unique attribute? Doesn't this look like some kind of a hack? The answer is certainly not. We will look further on this.

We need to understand how NHibernate looks at a mapping file and interprets an association defined by us as <many-to-one> or <one-to-one> or <many-to-many>. Every association has two ends. Be it <one-to-one> or <many-to-one> or <many-to-many>, every link has two ends to it. In NHibernate when we specify <one-to-one> or <many-to-many> or <many-to-one> etc, WE MAP ONLY ONE END OF THIS LINK AND THAT END IS THE TARGET OF THE DEFINITION i.e <....-to-TARGET END>. So cheekily saying it, you could as well say in NHibernate mapping file, <something-to-one> or <something-to-many> . NHibernate always operates on only the target end of the link when an association is defined in a mapping file. Hence FOR A BIDIRECTIONAL ASSOCIATION, we need to map both ends of a link in two separate mapping files for each end of the link. Now in our earlier mapping when we used <many-to-one> mapping, we were actually operating on the <......-to-one> end only i.e the Payment end only. So when we constrained it with "UNIQUE" attribute, We actually ensured that the foreign key from the target end table i.e PAYMENT table, will be unique without repeats thereby ensuring that the association becomes <one-to-one>.

Now what do we do if we want this to be a bidirectional link?

We need to map the other end of the association also. To do this we use the Payment.hbm file.

Now Lets look at how the mapping is done. Please refer to the figure in IV. Look at the green arrow to see how the stored PaymentApprovedOrder reference is mapped from C# code to its mapping file.

The code below shows a snippet from Payment.cs and Payment.hbm to show the bidirectional link:

In Payment.CS file having the Payment class,

public virtual PaymentApprovedOrder PaidOrder { get; set; }

In Payment.hbm file,

<one-to-one name ="PaidOrder" lazy="false" class ="PaymentApprovedOrder" property-ref="OrderPayment"/>

In the mapping file for Payment, we see that the other end of association is defined exactly the way we like to see, a <one-to-one> mapping. Look at the purple arrow to see how attributes of this mapping is set. Especially one particular attribute called "property-ref". The <One-to-One> mapping has an attribute called "property-ref". To this "property-ref" attribute we set the reference of the other side of the link i.e the instance "OrderPayment" to signify that we have already mapped that end in the other mapping file and this end is the inverse end of it. NHibernate will know how to manage it.

Image 4

FIGURE IV

Now we have completed the One-to-One association. Similarly we will complete <many-to-one> and <many-to-many>, optional associations, join tables and collections of value types in this 8 part article series.

The "lazy" attribute

For clarity, i have removed the attribute "lazy=false" from figure IV but you will see it the code snippets presented from mapping files. What is this "lazy" attribute? Briefly, a object oriented system consists of a web of objects interconnected by associations. All these objects are made persistent to database. So when we need to fetch information for an object from database, we may endup bringing down an avalanche of objects because the object we need, will have associations or collections which inturn may have other associations and everything has to be fetched from database just to fetch one single object fully. Worse thing is in the end we may not use any of the associations or collections on the object we fetched. To avoid this avalanche, by default NHibernate uses "lazy=true", to lazily fetch all the associations and collections of the object at a later time if needed and to currently fetch only the object immediately in use, in which case only the object is returned with proxies or placeholders for its collections and many associations. In our case we know for sure we are not going to cause an avalanche of fetches from database so, we say lazy=false. Note that lazy=false is convenient but the best option always in real world scenario is lazy=true.

But Setting "lazy=true" here in the sample application will cause an exception. Why? I will tell you the reason. Please refer to the code snippet in Figure V. We have a generic convenience class called DBRepository<...> where we open a Session object to NHibernate and do some operations with Database and close the session object at the end. For example, when we want to retrieve a object by identifier, we use the DBRepository<...>.getItemById(...). If lazy=true, the object is retrieved with proxies or placeholders for associations and collections and returned to the calling method when this DBRepository<...>.getItemById(..) is called. But if you look at DBRepository<....>.getItembyID(...), we close the session object at the end of method call before exiting. So when the object retrieved from DB using DBRepository<...>.getItembyID(...) is consumed by using one of its associations or collections, as the lazy initialization feature is on (because lazy=true attribute is set) it will only have proxies for its associations and collections and hence will require the session object to go and fetch the information from database. Unfortunately the session object is already closed and hence an exception is thrown. So the sample must be re-architected for production use if lazy=true must be set. In this eight part series I will also show how this can be done. But the simplest solution for One-to-One association is setting lazy=false like I have shown in the code snippet here. We will later look at sophisticated solutions.

C#
T IRepository<T>.getItemById(long item_id)
{
    // THIS METHOD IS IN DBREPOSITORY<T> class
    T temp;
    using (ISession session = NHibernateHelper.Create().Session)
    {
        temp = session.Get<T>(item_id);
    }    
    return temp; //NOTE : AT THIS POINT WHEN METHOD RETURNS SESSION IS DELETED.

Figure V

The sample Microsoft Visual Studio 2012 project in Article 7 of this 8 part series shows these association mappings in a downloadable project. You may or may not need to change the ConnectionString. The client project in the code contains the sample code required to see each of the associations and mappings separately . All samples use POCOS. So they are fairly easy to comprehend. Article 1-5 will have the full sample download of article 7 embedded in them.

Conclusion

In future articles of this series, we will see mapping Many-To-One,Many-To-Many associations using NHibernate, Inheritance mapping using NHibernate, Effective use of NHibernate in production scenarios and handling collections using NHibernate will be the next step. Enjoy NHibernate.

PART 2

PART 3

PART 4

PART 5 A

PART 5 B

PART 6

PART 7

PART 8

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
India India
Software Developer developing in c#.net.

Comments and Discussions

 
GeneralMy vote of 5 Pin
IInjac12-Oct-12 2:49
IInjac12-Oct-12 2:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.