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.


Thursday, August 23, 2007 4:52:01 PM (Central Standard Time, UTC-06:00)
You have a memory leak there, any time a new CustomerList is created, it is subscribing to the static event, which will hold a reference to it forever, leading to memory leaks.
Even unsubscribing from the event on Dispose() will not help, because people forget this (or not do it in a finally clause), and trying to do it in the destructor will put a significant cost on your code because of the need to finalize so many objects.

I wouldn't suggest using this technique.
You may want to try exploring weak reference for subscribers, but that is far beyond simple
Thursday, August 23, 2007 5:55:00 PM (Central Standard Time, UTC-06:00)
Good point.
Friday, August 24, 2007 11:31:56 AM (Central Standard Time, UTC-06:00)
Rocky - Have you converted to C# and left us behind in the VB world? ;-)
VBMan
Wednesday, August 29, 2007 3:02:43 PM (Central Standard Time, UTC-06:00)
Hmmm? Hmmmm? Hmmmmmmmmmmm?
VBMan
Wednesday, August 29, 2007 8:54:43 PM (Central Standard Time, UTC-06:00)
No, not at all. I very conciously switch between VB and C# for each project or CSLA mod. Then I port changes to the other language.

When it comes to books though, the reality is that C# outsells VB 2 to 1. So VB is very worth it at 33% of sales, but C# has to get priority in this regard.
Friday, August 31, 2007 9:28:58 AM (Central Standard Time, UTC-06:00)
Wow... I did NOT realize that! I'd be prioritizing C# too if I were selling books... just good business sense.

I wonder... is the same 2-to-1 ratio true then for client projects? VB vs C# usage?? Is C# to .NET what VB was to COM? Is C# the new VB(Classic)?

Would love to hear your thoughts on this. Thanks!
VBMan
Friday, August 31, 2007 10:43:53 PM (Central Standard Time, UTC-06:00)
The VB/C# mix for actual work appears to vary a great deal based on geographic region and industry vertical. I don't think I can generalize effectively here, other than to say that I do subjectively think there's more C# work out there than VB work.

However, I think the dynamic language revolution/evolution/whatever may bite C# a little. VB, Ruby and Python (all in .NET) are slated to share a common dynamic language runtime, and that is _very_ useful when dealing with LINQ (among other things). Since C# doesn't appear to be embracing dynamic language features, it may become less favored for people who make heavy use of LINQ in their architectures - time will tell.
Saturday, September 01, 2007 7:19:51 AM (Central Standard Time, UTC-06:00)
There are some problems with your code :
1/ protected static virtual void OnCustomerSaved(Customer sender, Csla.Core.SavedEventArgs e)
you can not declare a method that is static and virtual in the same time!!

2/ protected override Customer Save()

{

Customer result = base.Save();

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

return result;

}

when you call base.Save() before OnCustumerSaved, this.IsNew is false and the object is not added to the collection.

I suggest :

protected override Customer Save()

{

Customer old = this.Clone();
Customer result = base.Save();

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

return result;

}
hatem_info
Tuesday, September 04, 2007 6:30:16 AM (Central Standard Time, UTC-06:00)
Thanks for the reply Rocky...

My own theory is this: (and it may be just that)

1. VB Classic devs: at least 50%+ migrated to VB.NET, the remaining % to C#
2. Java defectors to .NET; *always* choose C#
3. C++ migrators; see #2! ;-)

I think that simple pattern perhaps accounts for the fact that there seems to be more C# job postings. HOWEVER, I think that a true VB Classic dev turned .NET developer may actually be qualified for a higher percentage of jobs (biz apps) within the VB.NET postings than the C# postings. Here's why: for the VB.NET postings, it requires the usual things including perhaps knowledge of VB classic. The C# postings however, many times require knowledge and/or experience with C++ and/or Java thus reducing the number of C# postings a VB turned .NET developer would really qualify for. Thus... for a VB.com-->.NET dev there are actually more applicable positions they are qualified for in the VB.NET pool, even thought that total pool is smaller than the C# pool.

Your thoughts?
VBMan
Thursday, September 06, 2007 3:11:41 PM (Central Standard Time, UTC-06:00)
If (using this exmaple) the customer list was a singleton would that stop the memory leak?
Jud
Comments are closed.