In this chapter, we will be covering how to represent collections. There are different types of collections that we can use within the NHibernate such as −
Now, from the .NET perspective, we generally deal with lists or like very simple data structures, lists, dictionaries. .NET does not have a wide variety of different collection types. So why does NHibernate need all these different types? It really comes back to the database.
A list is an ordered collection of elements that are not necessarily unique.
We can map this using the IList <T>.
So although we might conventionally have a list of addresses, and from application point of view we know that the elements are unique, nothing in the list prevents us from inserting duplicate elements in that list.
A set is an unordered collection of unique elements. If you try to insert 2 duplicate elements into a set, it will throw an exception.
There's nothing specific in NHibernate about it.
It's just a convenient way a have a generic set implementation. If you're on .NET 4, you can use the new HashSet <T> to represent these, but in most NHibernate applications, we represent this is an ISet.
It is an unordered, if you pull back a list of addresses from a database or a list of orders, you don't know what order they're coming in unless you put in a specific Order by clause.
So in general, the data that you're pulling back from a database are sets.
They are unique collections of elements that are unordered.
Another common collection that we will see in the database world is a bag, which is just like a set except it can have duplicate elements.
In the .NET world, we represent this by an IList.
Sets are probably the most common, but you will see lists and bags as well depending on your application. Let’s have a look into a below customer.hbm.xml file from the last chapter in which Set orders are defined.
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
As you can see, we have mapped the orders collection as a set. Remember that a set is an unordered collection of unique elements.
Now, if you look at the Customer class, you will see that Orders property is defined with an ISet as shown in the following program.
public virtual ISet<Order> Orders { get; set; }
Now when this application is run, you will see the following output.
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6 Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7 The orders were ordered by: John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6 Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7 John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6 Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7 Press <ENTER> to exit...
If the items in the collection didn't need to be unique, if you could have multiple orders with the same primary key occurring multiple times in this collection, then this would be better mapped as a bag as shown in the following program.
<bag name = "Orders" table = "`Order`"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </bag>
Now, if you run this application you will get an exception because if we take a look at the customer class, you'll notice that the orders are marked as an ISet in the C# code.
So we will also need to change this to an IList and then here, we would need to change from the HashSet to a List in the constructor.
public class Customer { public Customer() { MemberSince = DateTime.UtcNow; Orders = new List<Order>(); } public virtual Guid Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual double AverageRating { get; set; } public virtual int Points { get; set; } public virtual bool HasGoldStatus { get; set; } public virtual DateTime MemberSince { get; set; } public virtual CustomerCreditRating CreditRating { get; set; } public virtual Location Address { get; set; } public virtual IList<Order> Orders { get; set; } public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; } public override string ToString() { var result = new StringBuilder(); result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus: {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating: {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince, CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:"); foreach(var order in Orders) { result.AppendLine("\t\t" + order); } return result.ToString(); } }
When you run the application, you will see the same behavior. But, now we can have an order occurring multiple times in the same collection.
John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286 Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287 The orders were ordered by: John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286 Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287 John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286 Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287 Press <ENTER> to exit...