Search  
Friday, September 10, 2010 ..:: Articles » CSLA version 2; what's in it for me? ::..   Login
 CSLA version 2; what is in it for me?
Introduction | Business Objects | Simplify UI | Data Binding | ORM | Business rules | N-level undo | Security | Scalability | Localisation | Best practice | Community | License | Links
 
Show as single page

Deliverable: Business Objects; smart and mobile

CSLA delivers two major contributions towards the use of Business Objects (BO) within our applications:

  • the framework makes it easier for us to make an effective BO, as it provides a very well thought out, and proven, design which removes most of the heavy lifting, deep level of skill, and drudgery that would otherwise be involved.

  • the framework solves the very complex task of how to design and build BO's which can be used by an application that is, or may become, deployed across multiple hardware tiers. This is achieved in a manner that doesn't lose the gains initially sought by adopting an object-oriented approach.

Firstly I would like to briefly discuss the case for using BO's in our applications. As you would expect with a title like “Expert 2005 C# Business Objects”, this topic is covered in great depth by Rocky's book. Much of the case can be summarised by those foundations of the Object-Oriented paradigm; encapsulation and reuse.

Our applications exist to deliver a union of our business data and our business rules to our users. We need the business rules to be available to our UI so that it can provide the users with a rich experience. The availability of the business rules within the UI allows us to give immediate validation and authorisation feedback to the user, without repetitive round-trip delays back to the server. In a desktop application this means that the business rules are available within the WinForm, and in an Internet application they need to be available to the Web Server.

If the application has an Application Server tier, the same business rules are required at that point also. The App Server is the central point of control before data makes it way out of, or into, our database. It needs the business rules to ensure that any UI has correctly and consistently applied validation and authorisation rules.

The business rules are also required to manipulate or interpret the business data within whichever tier is doing the actual work of servicing the user's request.

Even if our application has no current need for physical tiers it should be built with logical tiers. The application will be more easily and safely maintainable,with a longer useful life, if it is segmented into logical tiers. It also means that if we ever need to implement hardware tiers at some later point, the application design is ready for this. The tipping point may an increase in the number of users, a need to pool database connections, or just a need to move what was a desktop application onto the Internet.

The BO needs to be a mobile object, which can move between the various tiers, to meet the needs of each tier . The design and construction of the BO needs to take into account the laws of physics regarding the latency and overheads involved when tier computers are cooperating to complete a job. Careful design is required to avoid the killer performance penalty that will result if the underlying mechanism is overly chatty or inefficient.

Business Objects encapsulate our business data within our business rules. The only way to get or update the data is via this mobile BO, which we design to apply and enforce the business rules. This encapsulation and protection would be lost if our system was having to move data between the tiers in an unprotected container such as in a DataSet or an xml package.

CSLA BO's not only provide better encapsulation and protection for our data than these non-BO alternatives, but they are also more lightweight to move between the tiers. This is due to the fact that they are binary, and don't require the meta-data overhead of DataSets. This lightness is achieved by installing our BO class library's DLL on each of the computers involved, so that only the data needs to be serialised and transmitted. DLL deployment and version update issues can be handled by .NET's ClickOnce facility, now made easier by the Publish Wizard which is built into VS2005.

The reuse angle comes from the fact that the business rules within the BO are reused between tiers of an application, and can also be reused between applications. The case study that Rocky builds during the course of his book show a set of BO's being built then deployed in three separate application modes. Essentially the same application is deployed as a desktop application, a web server application, and then as a set of web services. The same set of BO's are reused for each application. This reuse achieves savings, and also ensures that all applications are applying the same consistent set of business rules.

As I said at the opening of this section, CSLA has two major contributions to make with regard to BO's. The first is the design and implementation of the CSLA framework to meet these challenges, which is a task requiring deep experience and exceptional knowledge of very complex corners of Microsoft's huge .NET framework. This skill level was required by CSLA's designer, but is not required of us to use and deploy the framework.

CSLA's second contribution in this regard is the depth and breadth of the CSLA framework itself. We are left with just the task of writing our business logic. Most of the logic which makes our class into an effective object comes already built into the CSLA framework. I can illustrate this via one of the classes that I use within the demonstration program that comes with the article that you are reading. It is a Customer Order Header class. This class diagram shows that I have implemented a few data properties allowing access to the object's business data. We can see that I have some methods involved with authorisation and the business rules used by the property setters. There are also a couple of methods involved with fetching and updating the order header.

This is all the work that I had to do, but as the following Class View listing shows there is much more to my Order class. This listing shows that my Order class has descended from a rich hierarchy of CSLA classes which enrich my Order class with a lot of other features. Not all of these methods are used by the UI programmer, but the listing demonstrates the breadth of what is delivered by the framework, Examples are the ability of an Order object to clone itself, the IsValid property, or the collection of broken validation rules.




Another benefit coming from the use of BO's is their suitability for unit testing. I don't know how to apply unit testing to a UI, but if we extract our business rules into BO classes it becomes very easy to write comprehensive unit test suites for these rules.

I don't have the up-market version of VS2005, so here is a demonstration using the free utility called NUnit (see links section of this document). The following image shows that I have run the suite of unit tests which have been defined for our BO's, and that one of these tests has failed. The listbox at the middle right shows an identifying error message, and informs us that a value of 88.88 was received, but a value of 36.68 was expected. The list box at the bottom right is showing that failure happened at statement 93 of our testing procedure.

The test suite project is part of our VS2005 solution. We can exhaustively test and re-test our business rules at any time, with a simple press of the “Run” button. If our business rules were scattered through the UI, rather than being centralised in BO's, this would not be possible. NUnit allows us to run all tests by clicking on the root node on the left hand side of the screen as I have done here. Alternatively we can elect to run a a single test, or a sub-set of tests by selecting nodes within the tree itself.

Now I will examine part of the test suite project itself. Firstly this will demonstrate how easy it is to create unit tests for our BO's. NUnit will automatically create a tree node for any method that is marked with the [Test] attribute. This is a facility to run test set up and clean up procedures before each test, or each group of tests. It has a selection of Assert statements to handle a variety of equality, identity, comparison and type tests. Tests can also be easily set-up that will fail unless a specific exception is raised by the code being tested.

The second point of including the following source code is to illustrate how easy the BO are to use. These test procedures deal with the BO's at a simple level to fetch, clone and update data. The programmer has not needed to be concerned with database table structures, ADO, nor SQL.

using System;
using CslaWIIFM2.library;
using Csla; 
using NUnit.Framework; 
 
namespace NUnitTests 
{ 
 [TestFixture] 
 public class OrderUpdateTests 
 { 
  // fields 
  Customer _customerFromUSA; 
  Customer _customerFromUSAClone; 
  Customer _customerFromElsewhere; 
  Customer _customerFromElsewhereClone; 
 
  private readonly string _updateDate = "27/09/2006"; 
  private readonly float _updateFreight = 36.68f; 
 
  /// <summary> 
  /// This setup routine runs before each test (methods marked with [Test] attribute. 
  /// </summary> 
  [SetUp] 
  public void Init() 
  { 
   _customerFromUSA = Customer.GetCustomer("HUNGC"); 
   _customerFromUSAClone = _customerFromUSA.Clone(); 
 
   _customerFromElsewhere = Customer.GetCustomer("BLONP"); 
   _customerFromElsewhereClone = _customerFromElsewhere.Clone(); 
 
   CslaWIIFM2.library.Security.SecurityPrincipal.Logout(); 
   CslaWIIFM2.library.Security.SecurityPrincipal.Login("Doris in Despatch", ""); 
   System.Security.Principal.IPrincipal user = 
   Csla.ApplicationContext.User; 
   Assert.IsTrue(user.Identity.IsAuthenticated, "Login failure durinit Init"); 
  } 
 
  /// <summary> 
  /// These methods runs after each test. 
  /// </summary> 
  [TearDown] 
  public void CleanUp() 
  { 
   CslaWIIFM2.library.Security.SecurityPrincipal.Logout(); 
   CslaWIIFM2.library.Security.SecurityPrincipal.Login("Doris in Despatch", ""); 
 
   /* Clone is the object value before the test. Save this object to restore the db 
    * to it's initial state. Need to make the clone appear changed, so update, then 
    * reset a property to it's initial value. */ 
   float xxx = _customerFromUSAClone.Orders[1].Freight; 
   _customerFromUSAClone.Orders[1].Freight = 0f; 
   _customerFromUSAClone.Orders[1].Freight = xxx; 
   _customerFromUSAClone.Save(); 
 
   xxx = _customerFromElsewhereClone.Orders[1].Freight; 
   _customerFromElsewhereClone.Orders[1].Freight = 0f; 
   _customerFromElsewhereClone.Orders[1].Freight = xxx; 
   _customerFromElsewhereClone.Save(); 
   CslaWIIFM2.library.Security.SecurityPrincipal.Logout(); 
 
  } 
 
  /// <summary> 
  /// One of my business rules is that freight is free within the USA.  
  /// This test ensure that the USA based Customer object becomes  
  /// invalid if a freight value is applied. It also tests that 
  /// non-USA customer can be charged freight.  
  /// </summary> 
  [Test] 
  public void TestFreeUsaFreightRule() 
  { 
   _customerFromUSA.Orders[1].Freight = 0.01f; 
   Assert.IsFalse(_customerFromUSA.IsValid,  
    "Ensure freight invalid for USA"); 
   _customerFromUSA.Orders[1].Freight = 0.00f; 
   Assert.IsTrue(_customerFromUSA.IsValid,  
    "Ensure reset USA freight to 0 makes object valid again"); 
 
   _customerFromElsewhere.Orders[1].Freight = 0.01f; 
   Assert.IsTrue(_customerFromElsewhere.IsValid,  
    "Ensure invalid freight rule only applicable within the USA"); 
  } 
 
  [Test] 
  public void TestOrderUpdates() 
  { 
 
   Order order = _customerFromElsewhere.Orders[1]; 
   Assert.AreNotEqual(order.Freight, _updateFreight,  
    "test needs these to start out as being unequal"); 
   Assert.AreNotEqual(order.ShippedDate, _updateDate); 
   order.Freight = _updateFreight; 
   order.ShippedDate = _updateDate; 
   _customerFromElsewhere.Save(); 
 
   Customer cu = Customer.GetCustomer(_customerFromElsewhere.Id); 
   Assert.AreEqual(cu.Orders[1].Freight, 88.88m, "Update error"); // <== cause error 
   //Assert.AreEqual(cu.Orders[1].Freight, _updateFreight, "Update error"); 
   Assert.AreEqual(cu.Orders[1].ShippedDate, _updateDate); 
  } 

Our BO's can have much more function than the simple ones that I have developed for this article. My BO's do little more than provide an simple editable view of our data. A real-world set of BO's would contain all of the functions that could be performed with our application. An example could be “amalgamate all unshipped orders for this customer”.


Introduction | Page 2 of 14 | Simplify UI

      

Copyright 2005 by Primos Computer Services   Terms Of Use  Privacy Statement