Some time ago, I wrote some code to handle colliding date ranges inside a collection.
As simple as it may seem, it's not as straight forward as some may think.
At that time I used some active objects features and worked very well and contemplated many different scenarios.
Since then, I thought about doing the same for CSLA classes, this time in a reusable way. So, here we go:
What cases does this cover?
-Two single items inside a collection can't have an overlapping date range (start/end dates).
-Items may or may nor be groupped (we'll come back to this later).
What does this mean?
Well, for the dates themselves we have 3 basic scenarios (with their inverse variations):
That's pretty obvious. So far so good.
Now, what are the "issues"? By issues I don't necessarily mean problematic issues, but more like things you need to take into account.
Issue #1 is that items inside a collection don't have references to each other.
Issue #2 is that you're adding items to the collection.
Issue #3 is that you're removing items from the collection.
Issue #4 is that the data in the items can and probably will change.
Note: For issue #1, you could access the "Parent" property, do some casting and get a reference to every other item, but that's not ideal. In this demo, I'm splitting the responsability of the validation between the items and the collection. A good reason for that is that you don’t want to revalidate a child against all others every time the rules are checked, only when the items are inserted /removed or when they actually change, and not for instance, when you call ValidationRules.CheckRules.
I’ll spare us all from the implementation details and leave that to those of you who are really interested and want to look at the code itself.
It is worth noting that the child has a state variable that tells it whether it’s conflicting or not.
There are 2 props (StartDate and EndDate) that expose the text of both smartdates. Also, for the purposes of testing, you’ll notice that I’m exposing the smart dates as readonly properties too. This is only to make testing easier, and they should be removed. They should not be used directly. (Their main purpose is to make sorting in the sample grid easier)
Now, a last feature that’s really important in all of this is grouping. Most of the times you don’t need to validate an item against every other item. Sometimes, there are categories. Let’s see a project tracker like example:
A project has resource assignments (let’s forget about roles for a sec). Resources may appear more than once in the project, as long as they don’t appear more than once in the same time frame. So, let’s say you assign “Joe” to a project from 2007-01-01 to 2007-02-28, and then from 2007-04-01 to 2007-05-31. That’s a perfectly valid scenario. You have a resource assigned to a project twice but with different start / end dates. The validation should only be run for Joe, and not against all resources.
That’s where grouping comes into play.
The child item has function called “GetGroupIdValue()” that you can override and that returns a default value for all items in case you don’t need grouping.
So, if you were to do this sort of validation by resource, you would override that function and return the assignment’s id in this scenario.
The function returns an object, so you can return anything there. In the case where you have more than one grouping key, you could create an object or structure that contains all the appropriate values and that overrides “Equals()” to correctly handle comparisons.
If your grouping key is subject to change, (For instance, you group by resource and role and the role may change in the child) there’s a delegate that takes care of it. You just need to trigger that delegate in order to notify the collection that the grouping for that item has changed. All you need to do is call “OnGrouppingKeyChanged()” in the base class from your property set and that’s it.
The collection keeps a hashtable of these groups that uses the grouping key as key and contains a List(Of T) that contains all the items in that group. The hashtable is not serialized and is recreated upon deserialization.
The sample code demoes all of these features, so if you’re interested, download the solution and try it out.
Note that I rewrote this from scratch about a month ago, so I might have missed something, If you notice anything strange, let me know.
The demo app uses the error treeview to simplify the ui and to help you find the items with problems faster. All the references are included.
Again, remember that SDate and EDate are only there to simplify sorting, they’re not editable in the grid.
Well, that’s it!! I hope I got you interested!
You can get the code
here.
Andrés
posted @ Thursday, May 03, 2007 8:13 PM