Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET 4.5
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Amazon Web Services
Analysis Services
Android
Architecture
Arduino
ASP .NET Web API
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
BDD
Big Data
Bing
BizTalk
Book Excerpts
Build and Deploy
Business Intelligence
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE Framework Info - non Technical
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Debugger
Design Patterns
Development Process
Display Technologies
Distributed Computing
Document Database
DotNetNuke
DSL
Dynamic Languages
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
HTML 5
Internet Explorer 8.0
Interviews
IOS
iPhone
Iron Ruby
Java
Java Script
JavaScript
jQuery
JSON
Lightswitch
LINQ
Linux
LUA
Mac OS X
MDX
Messaging
Metro
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
MVVM
MySQL
Network
NHibernate
node.js
NOSQL
Nuget
Object Oriented Development
Objective C
Odata
OLAP
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
PHP
Podcasts
Post Mortem
PowerPoint
Print/Output
Prism
Product News
Product Reviews
Project Management
Prolog
Python
Q&A
Rails
Rake
Razor
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Scheme
Search
Security
Services
SharePoint
SignalR
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server 2012
SQL Server CE/AnyWhere/Mobile/Compact
SSIS
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
TFS
Tips
TypeScript
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 11
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio 2011
Visual Studio 2012
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WebMatrix
WF
Whitepapers
Windows 7
Windows 8
Windows Azure
Windows Live
Windows Phone 7
Windows Phone SDK
Windows Server
Windows Vista
WinForms
WinRT
Workflow
WPF
XAML
Xiine Documentation
XML
XNA
XSLT



LearnNow


XAMALOT
 


SSWUG

Reader rating:
Click here to read 2 comments about this article.
Article source: CoDe (2009 Jul/Aug)


Article Pages:  1  2 - Next >


Relational Database Persistence with NHibernate, Part 2

This article continues from the May/June 2009 issue of CODE Magazine (Quick ID 0906081) which covered why you want to use NHibernate, techniques for configuring NHibernate, how to map your objects to your data entities, and how to load basic objects.

One-to-Many Collections

You now have the ability to load a given SalesOrderHeader and load its associated Customer object. What about loading all the SalesOrderHeaders for a given Customer? That’s called a one-to-many (OTM) relationship.

Since there will potentially be many orders for a customer, you will need a property on Customer whose type will be an IEnumerable-derived type or list structure of some kind (Array, IList<T>, IEnumerable<T>, and so on). This will allow you to for-each over the list of orders for a given customer, which would be the most natural thing from the .NET code perspective.

In order to map this situation to a database structure with NHibernate, you have to choose among five different types of list structures.

  • Set - This is similar to the mathematical “finite set” concept (i.e., a list with no duplicates which may or may not be ordered). This corresponds to the Iesi.Collections.ISet interface from the Iesi.Collections.dll assembly shipped with NHibernate. A separate assembly is required because there is no ISet available in the .NET Framework Base Class Library. ISet is not included in NHibernate because it is beyond the scope of what a persistence framework is responsible for. Thus, ISet has its own assembly.
  • Bag - A list of entities which may contain duplicates and which has no specific or intentional order. This type corresponds to the ICollection/ICollection<T> or IList/IList<T> .NET types.
  • Array - A plain, regular .NET array of a particular type.
  • Map - An IDictionary/IDictionary<T> whose key and value are specified by the mapping.
  • List - Another form of ICollection/IList (or their generic forms) similar to bag except the list index is specified by the mapping. A real-world example of this might be line items for an order where the index is the LineItemNumber column so that, when loaded from the database, NHibernate will set up the IList ordered by the LineItemNumber.

Bag and Set are perhaps the most commonly used list mappings. I would encourage you to try the others to see what fits best with your situation.

Adding the Collection to the Entity

Start by adding a property called “SalesOrders” to the Customer class of type IList<SalesOrderHeader> (IList(Of SalesOrderHeader) for the VB folks). Make sure to make it virtual. You should also consider initializing the IList in your constructor to a default empty List<SalesOrderHeader> in case you use your Customer object before it’s saved or loaded through NHibernate.

At this point, you may be wondering: “What if I don’t want an IList directly exposed to callers? What if I want the IList private and have accessor/mutator methods like AddOrder() or RemoveOrder()?” These are valid questions and NHibernate can handle them just fine. I’d like to demonstrate the simple case first, before I get into some more complicated aspects of mapping such as mapping private fields, and so on.

Mapping a One-to-Many Collection

Since the SalesOrderHeader table has no implied ordering (i.e., a LineItemNumber column), you can safely map the relationship using a <bag> or <set>. Bags are a little easier to get started with because they don’t involve having to mess with the ISet assembly reference hassle mentioned above.

Ultimately, I recommend you seriously consider using ISet for most one-to-many situations like this because it has the semantics most situations would expect. There are some downsides to using a <bag> that may trip you up later. However, in an effort to get started quickly, let’s map the “SalesOrder” property as a bag. You can change it later if you start running into problems.

You need three things to map a collection like this:

  • The collection type (bag, set, map, list, etc.).
  • The key column on the child table that points back to this entity’s ID (i.e., the CustomerID column in the SalesOrderHeader table).
  • The name of the child class that will be contained in the collection (i.e., SalesOrderHeader).

The one-to-many mapping from Customer to SalesOrderHeader will end up looking like this:

<class name="Customer">
  <id name="CustomerID">
    <generator class="native" />
  </id>

  <!-- <property> tags here -->

  <bag name="SalesOrders" inverse="true">
    <key column="CustomerID"/>
    <one-to-many class="SalesOrderHeader"/>
  </bag>
</class>

A Quick Note About Bi-directional Associations

You may have noticed that I slipped in an inverse=true attribute into that mapping. This is because you’ve now mapped SalesOrderHeader to Customer as well as Customer to SalesOrderHeader. You’ve mapped the relationship both ways (bi-directional) and, without the “inverse” attribute, haven’t indicated to NHibernate that these two relationships are, in fact, the same one. Perhaps in a future version NHibernate may grow smart enough to determine this automatically, but for now the problem can get complicated when more relationships are involved, so you have to give NHibernate some hints.

Without the “inverse” attribute, NHibernate will view these mappings as two, distinct relationships and treat them as such. Thus, when adding a new SalesOrderHeader to an existing Customer, NHibernate would likely INSERT the SalesOrderHeader and then issue a separate UPDATE statement to update its CustomerID field, thus relating it to the Customer. This is excessive because NHibernate actually has all the information it needs to issue one INSERT statement with everything set up properly.

In order to help NHibernate understand your intention, you must tell it that one side of the relationship is the “primary” one. For an OTM/MTO relationship, the OTM side (Customer to SalesOrderHeader) should be the “inverse” and the MTO side (SalesOrderHeader to Customer) should be the primary.

Saving and Updating OTM Collections

Now that I have the bi-directional relationship wired up correctly, I can talk about how to properly relate the objects together through code. Again, imagine what would be required to create this association purely in memory as if there were no database to worry about it. Likely, that code would look like:

var customer = createNewCustomer();

for (var i = 0; i < 5; i++)
{
    var order = createNewOrder("PO" + i);
    order.Customer = customer;
    customer.SalesOrders.Add(order);
}

Not surprisingly, except for the addition of a call or two to ISession.Save(), this is exactly the code that will create the proper structure for NHibernate to persist the relationship properly to the database. This goes back to some of my original points about a good O/RM not being too intrusive into how the object-oriented code works and functions while also not imposing unnecessary requirements on the relational database side.

By default, NHibernate will not do any cascading of operations (such as deleting a related entity when you remove it from the collection, etc.). NHibernate does, however, have very powerful and configurable cascade and life-cycle management features. I’ll go into more depth on these later. Let’s take a look at what the above code looks like after I add the NHibernate calls into it:

var customer = createNewCustomer();
session.Save(customer);

for (var i = 0; i < 5; i++)
{
    var order = createNewOrder("PO" + i);
    order.Customer = customer;
    customer.SalesOrders.Add(order);
    session.Save(order);
}

xaction.Commit();

Notice that the only difference is the two calls to session.Save(). After I cover the cascading options, I’ll show you how to get rid of one of those save calls.

Retrieving the Collection

By default, when you get a Customer from the database, the SalesOrders list will be an empty proxy for lazy loading purposes. When you attempt to enumerate over the collection (call foreach() or pass the list to some method that will end up looping over the list), the proxy will trigger a lazy load and then load all the related SalesOrderHeader objects. As I mentioned before, you can configure this behavior. This is only the default.

var customer = session.Get<Customer>(customerId);
foreachvar order in customer.SalesOrders )
{
    Console.WriteLine(order.PurchaseOrderNumber);
}
&

By: Chad Myers

Chad Myers is the Director of Development at Dovetail Software, Inc. in Austin, TX. He has over 10 years of software development experience creating elegant, functional, and durable Web-based enterprise software systems in Java and .NET/C#. Chad spends most of his professional time practicing Agile development techniques as well as object-oriented principles. He tries to spread as much knowledge has he has received by giving user group and conference talks, writing articles, and maintaining an active blog at http://chadmyers.lostechies.com.

chad@activewin.com



Article Pages:  1  2 - Next Page: 'Other Types of Relationships' >>

Page 1: Relational Database Persistence with NHibernate, Part 2
Page 2: Other Types of Relationships

How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
4.2 out of 5

4 people have rated this article.

Instantly Search Terabytes Of Text
“Lightning Fast”
– Redmond Mag
“Covers all data
sources” – eWeek
25+ fielded & full-text search options
dtSearch’s own document filters highlight hits in popular file types
Web Spider supports static & dynamic data
APIs for .NET, Java, C++, SQL, etc.
Win / Linux (64-bit & 32-bit)
www.dtSearch.com
 

      AppsWorld Europe

 

SSWUG