Wednesday, March 15, 2006

A Solution for the reference data problem

What is 'the reference data problem'? In our Winforms application we cache reference data at startup. We hold a reference to that reference data using the ReferenceData class for the lifetime of the application and use it to populate combo boxes and such like. But reference data also enters the application throughout its lifetime as properties of our domain objects. When we get a party object, it comes populated with a gender, a country and other reference data instances. The problem is that we now have multiple versions of the same thing. The party object has a gender, "Male", with an id of 2 (for example), but we also have our cached genders, which include a gender, "Male", with an id of 2. What's the problem with having multiple versions of the same thing? The main issue is that we can't do reference based comparasons; 'myParty.Gender is male' (where male is an instance of Gender). So, you say, of course we can compare the IDs 'myParty.Gender.Id = male.Id', and that's true, but the dot net framework doesn't know about our IDs, so if you try myGenderComboBox.SelectedItem = myParty.Gender it doesn't work as expected. A lot of stuff is much much easier to do if you only have one version of the 'Male' object. So how do we get around this problem? Well it's relatively simple, we just need a few things in place: 1. A way to mark our reference data classes as reference data. 2. A way to cache our reference data in a way that makes it easy to find. 3. A way to set all our domain object's properties that are reference data, to our cached instances. 1 and 2 are easy, 3 is a little bit more involved. To mark our reference data as reference data we can use an attribute. This was a nice from Anthony, one of my fellow developers. I created a new attribute in 'ReferenceDataAttribute'. Now we can just put this attribute on any reference data class, like this:
<ReferenceData()> _
Public Class Gender
    Inherits Entity

End Class
2 and 3 are solved by our old friend the ObjectStore (oops I need to blog about this!) and some changes to our ServiceProvider class. This is the class that provides instances of services; DAL or Web service proxies. We use it like this:
Dim partyService As IPartyService = _
            AIS.Service.ServiceProvider.GetServiceInstance(GetType(IPartyService))
I've used a variant of the Gang-of-Four 'Decorator' pattern here so that when we ask for a service instance, we don't get a reference to a DAL or Web service, we get a reference to a 'Filter' that then in turn holds a reference to the DAL or web service. The Filter intercepts all the calls to the service allowing it to do numbers 2 and 3 above; cache the reference data when it's first loaded and map domain object's properties back to the cached reference data objects as they are loaded. The class that actually does all the work is called ReferenceProvider. Each filter has one of these. They are created by a ReferenceProviderBuilder object which our ServiceProvider class holds a reference to. We use the ReferenceProviderBuilder property of ServiceProvider to set up a new ReferenceProviderBuilder at application startup. The ReferenceProviderBuilder creates and holds a reference to the ObjectStore that's used to cache and retrieve reference data objects and then hands the object store to every ReferenceProvider that it creates. The ReferenceProviderBuilder class also has a property 'Mode' which is a enum with two values; 'Load' and 'Filter'. When the state passed to a ReferenceProvider is 'Load' it caches any reference data it sees in the object store, when the state is 'Filter', it looks for any reference data references in the returned object graph and binds the property to the cached referenced data object instead. So what does the developer have to do to use it? 1. Attribute all their reference data classes with the ReferenceDataAttribute as above. 2. Make sure that those reference data classes are cached at startup. 3. Don't get the reference data instances from the Data Access Layer when building object graphs.

No comments: