Vibrant discussion about CSLA .NET and using the framework to build great business applications.
I'd like to get some opinions on how to handle the following situation. I have a property on a Customer object called StateCode. This is a string that establishes the US state for a customer (eg, NY). I also want to have a property on Customer called StateName that returns the full name of the state (in my example, New York). This would be a read-only property. What's the best way to load the StateName property when the StateCode property is set? I see a few options:
What I dislike about #1 is that an extract DB hit is taking place to load the state. In the case of data such as states which rarely if ever change I could use a pre-loaded list to find the state, saving the DB hit, but what if instead of StateCode the property relates to a very large table with ever-changing values?
What I dislike about #2 is that it's not as clean for data binding in WPF and Silverlight. I'd rather have a user select a StateCode from a dropdown list and have this set the StateCode property directly. This forces the loading of any read-only, display-only fields like StateName into the BO, not into the UI layer.
Don't take the example I've given as a perfect example. It merely demonstrates a basic point: if I have a business object A that has a reference to another business object B (not parent/child) and I want to have some read-only, display-only data available for performance reason in A from properties found in B, what's a good way to accomplish this?
Rocky provides an example in the ProjectTracker sample app that is somewhat related to this problem but it's different as I'll explain. Project has a collection of ProjectResource objects. When a resource is assigned to a project, the ProjectResource has a NewProjectResource method that takes the ID (as an int) of the resource as a parameter. This in turn calls a special Child_Create method, passing the int, which loads the Resource object and extracts the resources FirstName and LastName properties and stores them in ProjectResource, presumably for the same reason I need StateName - as read-only, display-only fields. However, the difference here is that this is occuring on the server-side where DB access is occurring and because there is no opportunity to change the ResourceId later on (it's a read-only property in ProjectResource), so loading this info up when the object is created (and loaded) makes perfect sense. In my case, however, the user is able to change the StateCode property after object creation so I need to keep these display-only fields up-to-date.
To start with, don't try to come up with a general solution that covers static and volatile data - that way leads to madness :)
Static data comes in two flavors: app-wide and per-object.
For app-wide static data like state codes, using a cached NVL can be a good solution. You can often use the static cache approach I show in ProjectTracker and discuss in the Core 3.8 video series. It is easy, cheap and keeps your code simple.
In this case the StateName property of the business object is just a lookup into the NVL based on the StateCode property value - so you never need to store the value or anything.
For per-object static data (so the data is unchanging, but is unique on a per-object basis), things are a little more complex. You might consider having the same app-wide static cache in memory and using it to locally create filtered lists as needed by each business object.
As an example, think about a category list for products. Once the user indicates that a product is for international sale, that might restrict the available categories - so the data is essentially per-object. If you have the full list in memory, it is a simple LINQ query to create your object-specific list. You could abstract all this into an NVL class that uses a RunLocal create method to run that LINQ query against the full cached list.
Alternately, if you can't realistically cache the master list, I usually either load the per-object data as part of the editable object's Fetch operation, or use a unit of work object to retrieve the two related objects. Personally I think the combined Fetch is usually simpler and cleaner, since the business object usually needs the per-object list to do business logic anyway...
Then there is volatile data that can't be cached because it changes too often. I suppose this comes in per-app and per-object versions too. But the point is that you need to get the data when you fetch the editable object - or even later in the process - because it is in flux. And because it is in flux, anything the user has in memory on the client is just an approximation of the real data anyway - so you never really know if the data is any good until you are in the transactional Update process on the server.
Volatile data that is per-object is often best loaded as part of the editable object's Fetch operation. For other volatile data a unit of work object is usually the best solution.