OPF3 - How to make relations between objects & tables

Treating tables as objects in my applications is a time saver. I don't have to worry about updating SQL statements, or even sanitizing my data against SQL injections. OPF3 takes care of all this very nicely for you.

Now typically, loading up an object from the database is the most you'll need to do, but like 100% of all the good databases out there, you'll have a relationship between two tables. Depending on the database you can have either a one-to-many or a many-to-many relationship. OPF3 has support to create these same relationships in your code. Best of all the relationships utilize lazy-loading, so they'll only be pulled from the database when they're called.

For our purposes we're going to have three tables (Orders, OrderItems and Customers) in our database. These three tables represent a majority of table relationships out there.

relations

 

Our primary focus will be on the Orders table. This application design allows an Order to have only one customer at a time, but with many OrderItems. We'll start with the simple customer relationship.

OPF3 uses the ObjectHolder class to manage relationships. Within our Order class we'll define a private variable for our relationship.

private ObjectHolder<Customer> _CustomerRelation = new ObjectHolder<Customer>();

We now have an object to hold our relation but we need to define exactly what our relationship is. OPF3 does not, and can not guess your column names. Here's where we use the Relation attribute.

[Relation( "CustomerId=Id", PersistRelationship = PersistRelationships.ChildFirst )]
private ObjectHolder<Customer> _CustomerRelation = new ObjectHolder<Customer>();

Now basically the relation attribute defines our relationship between the Order class and Customer class. This is specified by the "CustomerId=Id". Next, since an Order cannot exist without the Customer, the PersistRelationships.ChildFirst enum tells OPF3 that the Customer relationship should be saved / updated into the database before the Order. Last but not least, we create a property to access our relationship. This just removes one step when accessing the object. The final result looks like this.

[Relation( "CustomerId=Id", PersistRelationship = PersistRelationships.ChildFirst )]
private ObjectHolder<Customer> _CustomerRelation = new ObjectHolder<Customer>();
public Customer CustomerRelation
{
    get { return _CustomerRelation .InnerObject; }
    set { _CustomerRelation .InnerObject = value; }
}

Now the relationship for the OrderItems is a one-to-many relationship. Instead of accessing a single object like the customer, we'll be accessing a enumeration of OrderItems. Instead of using the ObjectHolder class, we'll utilize the ObjectSetHolder class, and our property will return an ObjectSet (collection of Persistent objects). Here's what the result will look like.

[Relation("Id=OrderId", PersistRelationship = PersistRelationships.ParentFirst)]
private ObjectSetHolder<OrderItem> _OrderItemsRelation = new ObjectSetHolder<OrderItem>();
public ObjectSet<OrderItem> OrderItemsRelation 
{
    get { return _OrderItemsRelation.InnerObject; }
    set { _OrderItemsRelation.InnerObject = value; }
}

Three things to note here. First, the PersistRelationships is set to ParentFirst. This is because the parent (Order) must exist within the database before the OrderItems can. Second, the ObjectHolder is replaced with ObjectSetHolder, and third the property returns an ObjectSet instead of a single object. Now in your code your can access and manipulate relationships as easy as this

Order loadOrder = _context.GetObject<Order>( "Id={0}", 15 );

string message = "Dear " + loadOrder.CustomerRelation.FirstName + " " + loadOrder.CustomerRelation.LastName + ". Here is a summary of your order." + System.Environment.NewLine;

foreach(OrderItem item in loadOrder.OrderItemsRelation)
{
     message += item.ProductRelation.Name + " x " + item.Quantity + " @ " + item.ProductRelation.Price.ToString( "C" ) + " = " + ( item.Quantity * item.ProductRelation.Price ).ToString( "C" ) + System.Environment.NewLine;
}

//TODO send an the message via email

Inserting and Updating

Now while these relationships are awesome for loading, they're fairly straight forward for persisting to the database. Lets pretend that your shopping cart was going to create the order, customer and order items all at once. Here's what it would look like in your code.

Order newOrder = new Order();
newOrder.EmployeeId = CurrentUser.Id;
newOrder.OrderNumber = GenerateOrderNumber();
newOrder.OrderDate = DateTime.Now;
newOrder.OrderStatus = ( int ) eOrderStatus.New;
newOrder.Status = 1;

newOrder.CustomerRelation = new Customer();
newOrder.CustomerRelation.FirstName = txtFirstName.Text;
newOrder.CustomerRelation.LastName = txtLastName.Text;

newOrder.CustomerRelation.Address1 = txtAddress1.Text;
newOrder.CustomerRelation.PostalCode = txtPostalCode.Text;
newOrder.CustomerRelation.City = txtCity.Text;

newOrder.OrderItemsRelation = new ObjectSet<OrderItem>();
newOrder.OrderItemsRelation.Add( new OrderItem { ProductId = 1, Quantity = 3 } );
newOrder.OrderItemsRelation.Add( new OrderItem { ProductId = 4, Quantity = 1 } );
newOrder.OrderItemsRelation.Add( new OrderItem { ProductId = 5, Quantity = 6 } );

try
{
    using(Chili.Opf3.Storages.Transaction t = _context.StartTransaction())
    {
        _context.PersistChanges( newOrder );
        t.Commit();
    }
    //Success with the order, redirect
}
catch ( Exception ex )
{
    MessageDisplay( ex );
}

Wrap it all up in a transaction and you're all set. OPF3 will find the relationships you created and insert the objects in this order: CustomerRelationship, Order, and finally OrderItemsRelationship.

Post a comment