Sie sind auf Seite 1von 7

Mapping Object Relationships - QuickStart with NHibernate (Part 3) One of the first things most users will try

to do with NHibernate is map an object relationship - either a parent/child, many-to-many, or a lookup value. Over the next several tutorials, we'll be expanding the ACME Widget Company's console application to add new features, each showing a different type of mapping you may encounter. For our first example, we will start with a very basic many to one entity map. ACME sells a wide variety of products, each with a specific unit of measure - for example, you may buy some items by the pound, others by the gallon, and others by the can or box. For the purposes of this tutorial, we will assume that a product can only have one unit of measure. Within our database, this would be represented by a normalized lookup table, with it's primary key a foreign key in our products table. Since the unit of measure does not stand on it's own as an aggregate object, this is a very simple relationship, with the product being aware of the unit of measure object, but the unit of measure needing no awareness of the concept of a product. Setting up our ProductRepository With the addition of support for units of measure, we're going to need to extend the capabilities of our Product repository, which means that the default IRepository interface is going to be insufficient. So, we'll begin by stubbing out a new IProductRepository interface in our ACME.Repository project. This will implement IRepository since we'll still use the basic methods afforded by the IRepository interface. Create a new interface class file named IProductRepository.cs as shown below. Note that you will have to add a reference to ACME.Model as well.

using System; using ACME.Model; namespace ACME.Repository { public interface IProductRepository : IRepository<Product,Int32?> { } }

Next, we need to have our current ProductRepository reference the new interface instead of the generic repository interface:

public class ProductRepository : IProductRepository

And finally, we need to change our SampleMethod routine to accept an IProductRepository object instead of a generic repository:

private static void SampleMethod(IProductRepository productRepo)

Implementing the UnitOfMeasure object Now, we'll go ahead and begin extending our new Product Repository to handle unit of measure support. We'll begin by creating a new map and POCO for our Unit of Measure. These are very simple objects, and as noted before, the Unit of Measure will have no awareness of the concept of a product (i.e. we would not need the capability - at least not yet - of saying 'show me all products that are sold by the pound'); Go ahead and create the class shown below in ACME.Model and name it UnitOfMeasure.cs:

namespace ACME.Model { public class UnitOfMeasure { public virtual int? UomId { get; set; } public virtual string UomDescription { get; set; } } }

Next, create a new mapping file named UnitOfMeasure.hbm.xml in ACME.Data. Don't forget to set it to be an embedded resource:

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="ACME.Model" namespace="ACME.Model"> <class name="UnitOfMeasure" table="UnitsOfMeasure"> <id name="UomId"> <generator class="identity"/> </id> <property name="UomDescription" length="20" /> </class> </hibernate-mapping>

We now need to have our Product POCO properly reference the Unit of Measure. Our first step is to add a UOM property to our Product POCO:

namespace ACME.Model { public class Product { public virtual int? ProductId { get; set; } public virtual string ProductName { get; set; } public virtual UnitOfMeasure UOM { get; set; } } }

And finally, we add the reference to our map. For this, we will introduce a new tag - the <many-to-one> tag. This tag essentially lets us use a property of our class to map to the primary key of another persistent class:

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="ACME.Model" namespace="ACME.Model"> <class name="Product" table="Products"> <id name="ProductId"> <generator class="identity"/> </id> <property name="ProductName" length="50" /> <many-to-one class="UnitOfMeasure" name="UOM" column="UomKey"/> </class> </hibernate-mapping>

The class attribute tells NHibernate what persistent class this property will map to. We actually do not need this attribute, since NHibernate can infer the class by the variable type we declare in our POCO, but it is being included for completeness. The name attribute is the name of our property. The column attribute is the column in the table we are mapping to that holds the foreign key to the class we are mapping a relationship to. (The names of the attributes above were chosen for demonstration purposes, not for good naming conventions).

If you were to run your application and output your SQL statements, you will see how NHibernate has interpreted our mapping files and objects, and even added a foreign key constraint for us:

create table Products ( ProductId INT IDENTITY NOT NULL, ProductName NVARCHAR(50) null, UomKey INT null, primary key (ProductId) ) create table UnitsOfMeasure ( UomId INT IDENTITY NOT NULL, UomDescription NVARCHAR(20) null, primary key (UomId) ) alter table Products add constraint FK4065562A6C59151D foreign key (UomKey) references UnitsOfMeasure
Our next step is to build in the capability to add new Units of Measure through repository class. First, we'll need to add a new method contract to our IProductRepository interface:

namespace ACME.Repository { public interface IProductRepository : IRepository<Product,Int32?> { void SaveUOM(UnitOfMeasure saveObj); } }


Then, add the required method to our ProductRepository class:

public void SaveUOM(UnitOfMeasure saveObj) { using (var session = GetSession()) { using (var trans = session.BeginTransaction()) { session.SaveOrUpdate(saveObj);

trans.Commit(); } } }
At this point, we can now modify our sample application to add a few units of measure, and use them when adding and editing our products:

private static void SampleMethod(IProductRepository productRepo) { SessionProvider.RebuildSchema(); //Add some units of measure var uomCan = new UnitOfMeasure {UomDescription = "Can"}; var uomBottle = new UnitOfMeasure {UomDescription = "Bottle"}; productRepo.SaveUOM(uomCan); productRepo.SaveUOM(uomBottle); //Create a Product var pNew = new Product { ProductName = "Canned Salmon", UOM=uomBottle }; productRepo.Save(pNew); //Get a Product var pGet = productRepo.GetById(pNew.ProductId); //Update a Product pGet.ProductName = "Canned Tuna"; pGet.UOM = uomCan; productRepo.Save(pGet); //Delete a Product productRepo.Delete(pNew); }
We'll go through this section by section. First, we're going to create two new UnitOfMeasure objects, and persist them to our database:

SessionProvider.RebuildSchema(); //Add some units of measure var uomCan = new UnitOfMeasure {UomDescription = "Can"}; var uomBottle = new UnitOfMeasure {UomDescription = "Bottle"}; productRepo.SaveUOM(uomCan); productRepo.SaveUOM(uomBottle);

(You should see the SQL code below output to your console) NHibernate: INSERT INTO UnitsOfMeasure (UomDescription) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Can' NHibernate: INSERT INTO UnitsOfMeasure (UomDescription) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Bottle'

Next, we will create a product record - but this time, note that we are providing a UnitOfMeasure object as a property, letting NHibernate figure out which foreign key to persist in our UomKey field:

//Create a Product var pNew = new Product { ProductName = "Canned Salmon", UOM=uomBottle }; productRepo.Save(pNew); (You should see the SQL code below output to your console) NHibernate: INSERT INTO Products (ProductName, UomKey) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Canned Salmon', @p1 = 2

Updating a product yields similar results:

//Update a Product pGet.ProductName = "Canned Tuna"; pGet.UOM = uomCan; productRepo.Save(pGet); (You should see the SQL code below output to your console) NHibernate: UPDATE Products SET ProductName = @p0, UomKey = @p1 WHERE ProductId = @p2;@p0 = 'Canned Tuna', @p1 = 1, @p2 = 1

Summary In this tutorial, we covered a very basic technique for mapping a relationship between objects. While this may be a simple example, it will also be one you use frequently as

you build presentation models that denormalize data from the normalized tables of your persistent entities. If you have questions or comments on this article, or have suggestions or technical corrections, you can reach me via email at rbpalmer2222@gmail.com, or follow me on twitter at @BobKnowsCodeFu. You can also visit my blog at http://www.geekswithblogs.com/BobPalmer

Das könnte Ihnen auch gefallen