Pages

Monday, August 10, 2009

Using Custom Entities in ASP.NET Applications – Part 2



This is the second part of my short series on using Custom Entities in asp.net applications. In this part we will see how association between custom entities is defined. We will also explore the different types of association between which can exist between custom entities.


Association between Entities

In the database world, relationship is a mandatory feature. Different tables are linked to each other through relationships. Relationships are defined by the help of primary keys. These primary keys are placed as reference (foreign keys) in other tables. In OO programming, this relationship between entities (or classes) is known as association. Each custom entity is uniquely identified by an entity identity or identifier. This entity identity maps to the primary key in the database.

In custom entities, an association is defined by placing the identity of one entity in the other. For example lets assume two entities Book and Author. Since a book always has an author (not to mention how many) we can define this association by placing the author identity in the book entity as show in listing 1:


Listing 1


public class Book
{
private int bookID;
private string title;
// other properties

private int AuthorID; // association

// other constructs
}



Association between Entities

In the following sections, we will look at the different types of association, also known as cardinality between entities. For this purpose, I will use the following entities:

• Customer
• Orders
• Products
• Suppliers



One-to-Many Association

In this type of association, one instance of an entity type is associated with multiple instances of the second type. For example, one Customer can have many Orders. In this case, the Customer is the owner of this association. So the Customer entity specifies the mapping by having a child list object of type Order as shown in listing 3:

Listing 3


public class Customer
{
private int CustomerID;
// other properties

private IList OrderCollection; // 1:M association
}



Many-to-One Association

In this type of association, multiple instances of one entity type are associated with a single instance of another type. For example, many Orders (owner) belong to one Customer. So each Order specifies the mapping by including the identity of the related Customer as shown in listing 4:

Listing 4


public class Order
{
private int OrderID;
// other properties

private int CustomerID; // M:1 association
}



Many-to-Many Association

In a many to many association, multiple instances of one entity type are associated with multiple instances of the other type. For example, many Products have many Suppliers and vice versa. In this case, both entities are the owner and have a child list collection of the related entity as shown in listing 5:

Listing 5


public class Product
{
private int ProductID;
// other properties

private IList SupplierCollection; // M:M association
}

public class Supplier
{
private int SupplierID;
// other properties

private IList ProductCollection; // M:M association
}



One-to-One Association

One to One association is not very common and exists in special cases. For example, if a database table is very large and most of the fields are rarely used then the database designer tends to separate out these fields in a different table to improve performance. But both the tables are still linked through the same identity. In custom entities, such association is presented by a single entity which has all the fields.


Using an Identifier or a Full-Blown Object

One question commonly asked on different forums is that when defining an association, should we use the entity identity (a primitive data type) or a full blown object of the entity being referenced. In other words, listing 1 can be rewritten as following:

Listing 6


public class Book
{
private int bookID;
private string title;
// other properties

private Author author; // association – Full Blown Object

// other constructs
}


Watch closely, in the above listing, we are not using the simple AuthorID of type integer to define the mapping. Rather we have used a child object of type ‘Author’. This is one approach taken by developers. To me, the answer to the above question depends on different factors including the technology being used plus the user requirements. Let me explain both these scenarios in details.

First, ASP.NET comes with many built in controls which make data binding a breeze. These controls including the ObjectDataSource, GridView, ListView etc are capable of binding to custom entities as well. However, these controls can only bind to properties of primitive types (int, string, bit etc). These controls cannot bind to complex child object or collection. To me, this is a short coming in the framework and hopefully will be removed in a future version. Although there are workaround, but they do not offer a concrete solution. So if the custom entity contains a reference of simple type, it can easily bind to a data control.

Second, the user requirement also plays an important role. Suppose we are creating an application for a retail outlet. A Customer makes an Order for different purchases. This customer may already exist in the system. Now if we have modeled our Order class to have a full blown Custom child object, we can easily create both the Order and the Customer object. Why because we have full access to the Customer object through the Order object. This way when the Order is passed down to the DAL, the entire Customer object is passed along and saved to the database. On the other hand, if the Order only had a reference to Customer identity, we first had to create a new Customer and then assign his identity to the Order object. This would be more like a two step process.

In the end, to me it remains a user preference whether to use a reference of simple or custom type. Different factors play in and have to be considered before preference is locked down.



Light-Weight Model and Association

When it comes to architecture, different people have different opinions (and they are justified as well). When defining association between entities, it tends to get very complex and difficult to handle. Custom Entities may have a deep hierarchy of association. For example, a Product is composed of Components. Each Component has many Parts and each Part may be made up of Sub-Parts. With such hierarchy, many issues have to be considered. For example, if we make changes at any level or the hierarchy, the entire hierarchy has to be persisted. Similarly, if we are making changes at any level and the parent hierarchy leaves the transaction, all the properties have to be restored back to the original state. There are numerous scenarios with such deep hierarchy.

An alternative to deal with such associations is by defining a light-weight model. This means that there is no need to have a child object with the parent object. We can instead use method (or more specifically services) to fetch the child data. This is more like a Service-Oriented Architecture where we tend to rely on services. The concept is to simply fetch the data when required.

For example, using the above approach, we can rewrite listing 1 as following:

Listing 7


public class Book
{
private int bookID;
private string title;
// other properties

public Author GetBookAuthor ()
{
//return GetBookAuthor (this.bookID)
}

// other constructs
}


In the above listing, an Author object is returned using the method GetBookAuthor. This eliminates the need to reference the Author object within the Book class. I am sure you can see the advantages of using this approach. However, my opinion is not final.


Summary

In this post, we saw the how association is defined between entities. We also looked at the different types of associations also known as ‘cardinality’. We also looked at the different scenarios which lead to deciding whether to use a reference of primitive type or a full blown object. In the next post, we will see how to code around custom entities. Stay tuned for more…

No comments:

Post a Comment