Rockford Lhotka

 Thursday, August 23, 2007
« Custom Principals and WCF | Main | Building Customer Intimacy Using Externa... »

I am working on my Using CSLA .NET 3.0 ebook and wrote some content that I don't think I'm going to use in the book now. But I don't want to waste the effort, so I'm posting it here.

The text discusses an answer to a common question: how do I get my read-only list of X to know that some X data has been changed. Obviously the following technique can't detect that some other user has changed some data, but at least it detects and handles the case where the current user changes some data that would impact an existing read-only list in that same process.

 

Another common example occurs when there is a read-only list that should be updated when an associated editable object is saved. For instance, you might have a CustomerList object that should be updated any time a Customer is saved.

To solve that issue, the CustomerList object needs to subscribe to an event that is raised any time a Customer is saved. Such an event should be a static event on the Customer class:

  public class Customer : BusinessBase<Customer>

  {

    public static event EventHandler<Csla.Core.SavedEventArgs> CustomerSaved;

 

    protected static virtual void OnCustomerSaved(Customer sender, Csla.Core.SavedEventArgs e)

    {

      if (CustomerSaved != null)

        CustomerSaved(sender, e);

    }

 

    // ...

 

    protected override Customer Save()

    {

      Customer result = base.Save();

      OnCustomerSaved(this, new Csla.Core.SavedEventArgs(result));

      return result;

    }

  }

The CustomerSaved event is declared using the standard EventHandler<T> model, where the argument parameter is of type Csla.Core.SavedEventArgs. The reason for the use of this type is that it includes a reference to the new object instance created as a result of the Save() call.

Then the Save() method is overridden so the CustomerSaved event can be raised after the call to base.Save().

The CustomerList class can then handle this static event to be notified as any Customer object is saved:

  public class CustomerList : ReadOnlyListBase<CustomerList, CustomerInfo>

  {

    private CustomerList()

    {

      Customer.CustomerSaved += new EventHandler<Csla.Core.SavedEventArgs>(Customer_Saved);

    }

 

    private void Customer_Saved(object sender, Csla.Core.SavedEventArgs e)

    {

      // find item corresponding to sender

      // and update item with e.NewObject

      Customer old = (Customer)sender;

      if (old.IsNew)

      {

        // it is a new item

        IsReadOnly = false;

        Add(CustomerInfo.LoadInfo((Customer)e.NewObject));

        IsReadOnly = true;

      }

      else

      {

        // it is an existing item

        foreach (CustomerInfo child in this)

          if (child.Id == old.Id)

          {

            child.UpdateValues((Customer)e.NewObject);

            break;

          }

      }

    }

  }

The final requirement is that the read-only CustomerInfo business object implement a LoadInfo() factory method, and an UpdateValues() method.

The LoadInfo() factory method is used to initialize a new instance of the read-only object with the data from the new Customer object. Loading the data from the Customer object avoids having to reload the data from the database when it is already available in memory:

  internal static CustomerInfo LoadInfo(Customer cust)

  {

    CustomerInfo info = new CustomerInfo();

    info.UpdateValues(cust);

    return info;

  }

The UpdateValues() method sets the read-only object’s fields based on the values from the Customer object:

  internal void UpdateValues(Customer cust)

  {

    _id = cust.Id;

    _name = cust.Name;

    // and so on ...

  }

The end result is that the CustomerList object is updated to reflect changes to the saved Customer object. The CustomerSaved event notifies CustomerList of the change, and (in most cases) CustomerInfo can update itself without hitting the database by loading the data from the Customer object that is already in memory.