In this post I’ll be taking a look at how we can set up mappings between regular .Net objects and database tables using Fluent nHibernate.
Fluent NHibernate offers an alternative to NHibernate’s standard XML mapping files. Rather than writing XML documents (.hbm.xml files), Fluent NHibernate lets you write mappings in strongly typed C# code. This allows for easy refactoring, improved readability and more concise code.
So to get started let’s consider the following simple data schema. A basic one to many relationship between a folder object and file objects as well as a parent child relationship between folder objects. Thus a folder can have folders as well as files, a folder can have a parent folder and a file belongs to a folder.
Creating Our POCO Objects
What we want to achieve here is a mapping between our database schema and a set of POCO objects that we can then create, query and manipulate within our code without the code needing to know anything about the details of the database with which they are being stored.
The first step in creating our mappings is to create simple objects that mirror the fields in our data schema. That is a Folder object and a File object with public getters and setters representing the fields present in our data tables.
{
public virtual int FolderId { get; set; }
public virtual string FolderLabel { get; set; }
public virtual int ParentId { get; set; }
}
{
public virtual int FileId { get; set; }
public virtual string FileName { get; set; }
public virtual Int64 FileSize { get; set; }
public virtual DateTime LastModified { get; set; }
public virtual int FolderId { get; set; }
}
One thing that it is important to keep in mind, is all properties on our objects must be declared as Virtual. The reason why properties need to be declared Virtual lies in the way nHibernate generates dynamic proxies to achieve its Lazy Loading feature. This is a bit beyond the scope of this post, but Davy Brion offers a pretty detailed explination,
The quick answer to that question is: because we need members to be virtual in order to do our lazy loading magic/voodoo.
The longer answer is more interesting though. An important feature that any real ORM must have is transparent Lazy Loading. If you retrieve an object through an ORM, you don’t want it to automatically pull in an entire object graph (not by default anyway), yet you don’t want to litter your code with checks to see if certain associations have been loaded yet, and then loading them if necessary. This is the ORM’s responsibility. Ideally, you want to be able to access properties and have the ORM load the necessary data upon first access of those properties if the data hasn’t been retrieved yet.
Other than this, there is nothing remarkable about our objects, nor should there be, they’ll just be used as DTO’s and nothing more.
Setting Up Our Mapping Objects
The next step is to tell nHibernate how our POCO objects relate the fields in our database, traditionally in nHibernate and Hibernate this was achieved via a xml mapping file. But using Fluent nHibernate we can create these mappings as strongly typed objects of their own.
Each of our tables/POCO objects will require a mapping object. At their most basic explanation, these objects will tell nHibernate what table to map each object to, what field to use as the primary key, and which properties map to which fields in our table.
Let’s take a look at how our FileMap object should look,
{
public FileMap()
{
Table("File");
Id(x => x.FileId, "FileId").GeneratedBy.Identity();
Map(x => x.FileName, "FileName");
Map(x => x.FileSize, "FileSize");
Map(x => x.LastModified, "LastModified");
Map(x => x.FolderId, "FolderId");
}
}
We can see here that our mapping object inherits from ClassMap, that is it is a ClassMap of type File which is the object we put together in the first step. The only other thing we need in this object is a constructor containing our mapping details.
Our first step, Table, tells nHibernate to map the File object to the table named File, from there we’re telling it that our primary key field is FileId and that this is generated by the SQL identity (An explanation of different methods of Id generation can be found here), lastly we are simply telling nHibernate how to map the remaining properties to our data base fields.
For the sake of completeness, here is how the FolderMap class should look,
{
public FolderMap()
{
Table("Folder");
Id(x => x.FolderId, "FolderId").GeneratedBy.Identity();
Map(x => x.FolderLabel, "FolderLabel");
}
}
Querying Our Objects Using Linq to nHibernate
Now that we’ve told nHibernate how to map our objects to our database, we can now simply query for these objects in code using Linq to nHibernate.
So let’s put together a repository for each of our objects and implement a simple fetch method on each to fetch a folder or file object by Id.
{
private ISession _session;
public FileRepository(ISession session)
{
_session = session;
}
public File Fetch(int Id)
{
return _session.Linq<File>().FirstOrDefault(f => f.FileId == Id);
}
}
{
private ISession _session;
public FolderRepository(ISession session)
{
_session = session;
}
public File Fetch(int Id)
{
return _session.Linq<Folder>().FirstOrDefault(f => f.FolderId == Id);
}
}
What’s happening in each of these repositories is firstly they are being constructed with an nHibernate ISession object, an ISession object represents a database session and is agnostic to the type of database we are working with. Constructing an nHibernate session is beyond the scope of this post, although I did touch on it briefly previously.
Secondly we are gaining full Linq functionality by calling the _session.Linq<T>() method, from there we can query our objects for whatever our requirements. In these cases we are simply querying for an object by Id.
That’s about as much as there is to the basics of mapping objects to data using Fluent nHibernate, one thing that is missing though is mapping our relationships. Currently our objects only contain a property referencing our foreign keys, it would be nice if our objects actually contained references to the objects for which the are related, and that will be the topic of my next post.
0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.