The approach to managing persistent data has been
a key design decision in every software project
we’ve worked on. Given that persistent data isn’t
a new or unusual requirement for .NET
applications, you’d expect to be able to make a
simple choice among similar, well-established
persistence solutions. Each of the competing
solutions has various advantages and
disadvantages, but they all share the same scope
and overall approach.
Having said that, making the right decision
requires a fairly good knowledge of the ORM
solutions currently available for .NET. In other
words, if all you know is one ORM and one only,
then you won't have much of a choice in there, you
always have to go with the only framework
that you know of and sometimes it might
not be the best solution for your specific
project. Entity Framework could be a viable
option, an approach to persistence, proposes to
provide a ORM solution with the full commercial
support and backing of Microsoft. But is that
really enough reason for us to simply ignore just
about everything else?
|
What is NHibernate and Why Should You
Care?
|
|
|
|
NHibernate
is an ambitious open source project that aims to
be a complete solution to the problem of managing
persistent data in .NET. NHibernate is a .NET port
of the very popular and successful Java
Hibernate
library and is one of the most mature, powerful
ORM frameworks available in .NET today, an
important option and one that you shouldn't
ignore.
|
What is the Goal of This Post?
From my experience with the .NET user community, I
know that the first thing many developers
(especially those with a history of using MS tools
and frameworks like EF) say when it comes to
NHibernate is that it's a sophisticated framework
and has a steep learning curve. In this series of
posts I'll walk you through the process of
building a project with NHibernate and will show
how you can leverage your EF knowledge to get up
and running with NH as quickly and easily as
possible.
This writing is not a debate on EF vs.
NHibernate, nor is a comparison to show which one
is better than the other. I'll just present the
advantages and drawbacks of the two approaches to
you and will show their solutions for various
persistence problems. My hope is that at the end
it gives you an insight to conclude which one is
a better persistence solution for your specific
project.
A Note For Those Who Never Worked with Any ORM
Before
Keep reading! The title of this series might be a
bit misleading as I don't assume any knowledge of
EF when explaining NHibernate, of course being
familiar with one ORM solution like EF would help
but is absolutely not a requirement so with a
little extra effort, you can learn NHibernate just
like the EF developers.
Your First NHibernate Project
Open your Visual Studio and create a new Console
Application project. Before you can start coding
your first NHibernate application, you need to
reference NHibernate binaries. NHibernate 3.1.0.GA
can be downloaded from
here. Download and extract the zip file to some
location on your drive. The next step is to
reference the required binaries in your new
project. To do this, follow these steps:
-
Right-click the project and select
Add Reference...
-
Click the Browse... button and navigate
to the folder where NHibernate binaries are
extracted.
-
Open Required_Bins folder and select
NHibernate.dll. Click OK to add this
reference to your project.
-
Right-click again on the the project and
select Add Reference... and navigate to
the folder where NHibernate binaries are
extracted.
-
This time open
Required_For_LazyLoading folder and
then Castle folder.
-
Add a reference to
NHibernate.ByteCode.Castle.dll.
Now that your solution is set up, you’re ready to
start coding your first NHibernate application!
|
|
Creating a Domain Model
Just like my other posts on EF Code First, this
post takes a top-down approach; it assumes that
you’re starting with a domain model and trying to
derive a new SQL schema but this time with
NHibernate. When you’re using NHibernate, entities
are implemented as POCOs, a back-to-basics
approach that essentially consists of using
unbound classes in the business layer which leads
to the notion of persistence ignorance in our
applications. This results in entities with less
coupling that are easier to modify, test, and
reuse. EF also supports this pattern starting from
its second version (aka EF 4.0).
|
|
|
The following shows the POCO classes that form the
object model for this domain:
|
public class User
{
public User()
{
Items = new List<Item>();
}
public virtual int UserId { get; private set; }
public virtual string Name { get; set; }
public virtual string Username { get; set; }
public virtual Address Address { get; set; }
public virtual IList<Item> Items { get; set; }
public virtual void AddItem(Item item)
{
item.Seller = this;
Items.Add(item);
}
}
public class Address
{
public virtual string Steet { get; set; }
public virtual string City { get; set; }
public virtual string ZipCode { get; set; }
}
public class Item
{
public Item()
{
Categories = new List<Category>();
}
public virtual int ItemId { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual User Seller { get; set; }
public virtual IList<Category> Categories { get; set; }
}
public class Category
{
public Category()
{
Items = new List<Item>();
}
public virtual int CategoryId { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Item> Items { get; set; }
public virtual void AddItem(Item item)
{
item.Categories.Add(this);
Items.Add(item);
}
}
|
The POCO classes are pretty straight forward,
except you might wondering what the AddItem
methods are for in there, for now, think of the
code in the AddItem (a convenience method) as
implementing a strong bidirectional association in
the object model.
How Mappings are Specified in NHibernate?
Just like EF, NHibernate gives you different ways
to specify mappings for your persistent classes.
You can use XML files, CLR attributes or Fluent
API.
-
Mapping using XML
The ability of mapping classes with XML is what
NHibernate inherits from Hibernate, where
usually each persistent class has an XML mapping
file ends with the .hbm.xml extension and is
embedded in the same assembly as the mapped
class file. This is analogous to an EF's Edmx
file but unlike Edmx files, these HBM mapping
documents are lightweight, human readable,
easily hand-editable and can be easily
manipulated by version-control systems. Although
Visual Studio comes with a designer support for
Edmx files, but the problem is that this support
is somewhat limited and you sometimes have to
drill down to the underlying XML file to define
your desired mapping or behavior, examples like
creating a
TPC
mapping or defining
QueryViews. I personally didn't find editing the raw XMLs
in Edmx files (and dealing with an Edmx file in
general) a fun practice and I think this is a
common feeling throughout the community as we
see more and more developers are leaning toward
Code First development
to keep their projects away from an Edmx file.
- Attribute-oriented Mapping
Alternatively, you can use CLR attributes
defined in the
NHibernate.Mapping.Attributes
library to provide all the information
NHibernate needs to map your classes. This is
comparable to the new attributes that the latest
release of EF (EF 4.1) provides in
System.ComponentModel.DataAnnotations
namespace which you can use to map your classes
when doing EF Code First development except that
EF annotations are not complete and you often
have to resort to fluent API to do the mappings
(Examples like creating a
TPC
mapping, customizing a
TPH
mapping, specifying cascade deletes on
associations or customizing the mapping for a
many-to-many association.).
- Fluent Mapping
Fluent mapping is based on a
Fluent Interface
that allows you to map your entities completely
in code, with all the compile-time safety and
refactorability that it brings. Just like the
fluent API in EF Code First,
Fluent NHibernate
project provides an alternative to NHibernate's
standard XML mapping files. As a result, rather
than writing XML documents, you write mappings
in strongly typed C# (or VB) code. It's even has
a concept called
Auto Mapping
which is a mechanism for automatically mapping
all your entities based on a set of conventions.
This is very similar to the EF Code First as
they both follow the
convention over configuration
principle. In this post we use Fluent NHibernate
to map our persistent classes since this allows
for easy refactoring, improved readability and
more concise and elegant code. More importantly,
if you used Code First fluent API before, you'll
find this approach very familiar.
|
Adding Fluent NHibernate to the Project
|
|
|
|
Fluent NHibernate is a separate open source
project that needs to be referenced in your
project before you can start using its fluent
interfaces to map your persistent classes. You can
download the binaries from
here. Once you've got a copy on your local machine,
right-click the project and select
Add Reference... and then reference the
FluentNHibernate.dll
assembly which is inside the extracted folder from
the zip file you downloaded.
|
Mapping Entities and Components
In EF Code First, we use
EntityTypeConfiguration
class to define a mapping for an entity. We derive
from this class to create a mapping, and use its
constructor to control how our entity is
persisted. Fluent NHibernate has the same approach
except that it differs between an entity and a
value object
in essence that if you are mapping an entity you'd
use
ClassMap
and if you are mapping a value object you'd use
ComponentMap
class. The reason for this naming is because
NHibernate uses the term component for a
user-defined class that is persisted to the same
table as the owning entity. A Component is the
exact same concept as a
Complex Type
in EF: an object that has no individual identity.
The following code shows how to register the
Address class as a component as well as how to
specify UserId as the object identifier:
|
class UserMap : ClassMap<User>
{
public UserMap()
{
Id(u => u.UserId);
Component(u => u.Address);
}
}
|
Associations in Entity Framework and NHibernate
As you may know, foreign keys in the first release
of EF were not exposed on entities, something we
know by the the name of
independent associations. These
associations were managed which means they had
their own independent entry in the
ObjectStateManager where each entry (of course)
has its own EntityState. These independent
associations make a few things such as N-Tier and
concurrency really difficult. For example,
concurrency checks were performed on relationships
independently of the concurrency checks performed
on entities, and there was no way to opt out of
these relationship concurrency checks. The result
was that your services must carry the original
values of relationships and set them on the
context before changing relationships. Because of
this and a few other problems, these associations
eventually became deprecated in the second release
of EF and a new type of association called
foreign key associations
has been introduced where foreign keys were
exposed in the persistent classes along with their
related object references (aka navigation
properties). The concurrency problem has been
solved because now the relationship is simply a
property of the entity, and if the entity passes
its concurrency check, no further check is needed.
Now you can change a relationship just by changing
the foreign key value.
I have mixed feelings about these foreign key
associations. On the one hand, it's not managed
anymore and doesn’t expose us to all the problems
of independent associations but on the other hand,
foreign keys are part of the
impedance mismatch
problem and should not be in the object model.
They add noise to the persistence classes and they
even introduce some new problems. For example,
having foreign keys as well as object references
for relationship navigation presents the problem
of two different artifacts representing one
relationship – this introduces complexity and now
you have to make sure that you keep these two in
sync.
On the other hand, the NHibernate association
model is interesting. Foreign keys correctly don't
need to be part of the entities but at the same
time NHibernate doesn’t "manage" persistent
associations. It's kind of the best of two worlds
approach. In NHibernate, if you want to manipulate
an association, you must write exactly the same
code you would write without NHibernate. If an
association is bidirectional, both sides of the
relationship must be considered. As you can see in
the model, the Employee class, for example,
doesn't reflect the ProductId foreign key from the
database. Next, you'll see how associations are
mapped in NHibernate.
|
Associations in NHibernate are Directional
It’s important to understand that NHibernate
associations are all inherently unidirectional. As
far as NHibernate is concerned, the association
from Item to User is a different association than
the association from User to Item. To EF
developers, this might seems strange; I’ll merely
observe that this decision was made because unlike
EF, NHibernate objects aren’t bound to any
context. In NHibernate applications, the behavior
of a nonpersistent instance is the same as the
behavior of a persistent instance.
Because associations in NHibernate are
directional, we call the association from Item to
User a many-to-one association (many Items
sold by one user, hence many-to-one). You can now
conclude that the inverse association from User to
Item is a one-to-many association. The
following shows the mapping code with Fluent
NHibrenate for the Item entity:
|
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(i => i.ItemId);
Map(i => i.Name);
Map(i => i.Price);
References(i => i.Seller)
.Column("UserId")
.Not.Nullable();
}
}
|
|
The
References
method creates a many-to-one relationship between
two entities. So far we have created a
unidirectional many-to-one association. The
column UserId in the Item table is a
foreign key to the primary key of the User table.
|
Making the Association Bidirectional
Now let's have a look at the mapping for the User
entity as the other end of the User-Item
association. Here we use the
HasMany
method to create a one-to-many association from
User to Item:
|
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(u => u.UserId);
Map(u => u.FullName).Length(50);
Map(u => u.UserName).Not.Nullable().Unique();
Component(u => u.Address);
HasMany(u => u.Items)
.Cascade.All()
.Inverse()
.KeyColumn("UserId");
}
}
|
The mapping defined by the KeyColumn method
is a foreign-key column of the associated Item
table. Note that we specified the same foreign-key
column name in this collection mapping as we
specified in the mapping for the many-to-one
association. Now we have two different
unidirectional associations mapped to the same
foreign key. At this point, all we need to do is
to tell NHibernate to treat this as a
bidirectional association. The
Inverse method tells NHibernate that the
collection is a mirror image of the many-to-one
association on the other side and we are done.
Mapping a Many-to-Many Association
HasManyToMany
method creates a many-to-many relationship and we
use it to map the association between Item and
Category entities. In NHiberbate, a bidirectional
association always requires that you set both ends
of the association and a many-to-many association
is not an exception:
|
public class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Id(c => c.CategoryId);
Map(c => c.Name);
HasManyToMany(c => c.Items)
.Cascade.All()
.Table("ItemCategory");
}
}
|
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(i => i.ItemId);
Map(p => p.Name);
Map(i => i.Price);
References(i => i.Seller)
.Column("UserId")
.Not.Nullable();
HasManyToMany(i => i.Categories)
.Cascade.All()
.Inverse()
.Table("ItemCategory");
}
}
|
|
When you map a bidirectional many-to-many
association, you must declare one end of the
association using Inverse method to define
which side’s state is used to update the link
table. I chose the Item entity to be the inversed
end of this many-to-many association. This setting
tells NHibernate to ignore changes made to the
Categories collection and use the other end of the
association— the items collection —as the
representation that should be synchronized with
the database.
|
NHibernate Configuration and Schema Generation
The object model and its mappings are ready and
now all we need to do is to wire them to
NHibernate. Fluent NH can help us with this as
well since it provides an API for completely
configuring NHibernate for use with your
application, all in code. The following shows how:
|
ISessionFactory factory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c =>
c.FromConnectionStringWithKey("SQLExpress")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Program>())
.ExposeConfiguration(config =>
{
SchemaExport schemaExport = new SchemaExport(config);
schemaExport.Drop(true, true);
schemaExport.Create(true, true);
})
.BuildSessionFactory();
|
Fluently.Configure starts the configuration
process. The Database method is where you
specify your database configuration using the
database configuration API. ExposeConfiguration is optional, but
allows you to alter the raw Configuration object.
BuildSessionFactory is the final call, and
it creates the NHibernate
SessionFactory instance from your
configuration. We'll take a closer look at
ISessionFactory and other NHibernate's core
interfaces in the next post.
As you may have noticed, this code also generates
the DDL from the object model and executes it
against the target database but with one caveat:
You need to manually create the database yourself
before running this code since NHibernate will
not create the database for you. Like the
class name suggests (SchemaExport), it only
creates the schemas in the database. After
creating the database, you can run this code as
many times as you want, as it is designed just
like EF Code First's
DropCreateDatabaseAlways
strategy.
SQL Schema
|
|
The presented object model will result in the
creation of the following schema in the database:
|
|
Source Code
Click
here
to download the source code for the project that
we have built in this post.
|
Summary
In this post, we take a high-level look at
NHibernate and its mapping system by running a
simple example. We also saw how to configure
NHibernate with Fluent NHibernate, a technique
similar to EF Fluent API which saves us from
dealing with XML mapping files. This post sets the
basis for the next part where we will focus more
on the dynamic behavioral aspects of NHibernate,
something that comes into play at runtime and
we'll see how it differs from EF in that area.
|