I've found myself in a bit of a predicament, but maybe you are the CSLA genious that can help! Offer a solution to this design problem and you can win the respect of your peers and an unlimited supply of bragging rights! Worth 42 points* (*points not available in all areas or in any areas at all. Points cannot be exchanged or returned. Actual monetary value of points: $0.00. Void where prohibited. Prohibited in current void.)
OK, so I admit that was a bit of a trick to get you to read my thread, but it's all true and you did find it at least a little amusing. Honestly, I'm excited to get everyone's feedback; here's the situation:
I'm working with a list of objects that we'll call "ActionFigures". This thread is not intended to discuss the merits of ActionFigures, only their implementation.
In the system there are different types of ActionFigures. ActionFigures do many common properties, but each disctinct type also has it's own distinct properties and behaviors as well. ActionFigures are stored in one table in the database to provide a unique ID, but periphery tables join to the root ActionFigure table to hold any data that is unique to an ActionFigure type or classification. This is just to avoid a sparce table. What I need to do is be able to determine if a user has a given ActionFigure based on given parameters - we'll call this an ActionFigure Query.
This lends itself to two implementations in my mind, but can you suggest another? First, I see retreiving all the user's ActionFigures from the database and using them locally (on the web server) to perform ActionFigure Querries. The second may be simply using a CSLA Command object to check against the database directly but I'm concerned that may be too "chatty" - perhaps caching the result of a particular ActionFigure Query would help.
For the first implementation - retrieving the whole list and querying locally - I'm thinking about a ActionFigureManager class. Provide the ActionFigureManager with the user information and it will retrieve all of the different ActionFigures for that user. Provide the loaded ActionFigureManager with information on an ActionFigure to look for (an "ActionFigure Query"), and it will tell you if it exists in the collection or not.
Remember though, that each ActionFigure type is a little different and has to retrieve different data from the DB from different table joins. This leads me to think that a single CSLA parent collection of child ActionFigure objects won't quite do it since different stored procs may need to be called to load and manage different ActionFigure data based on ActionFigure type (which is also stored in the DB). The data management behavior is different by type, however the other behavior should be the same: check against an ActionFigure Query. I'd love to make this ActionFigureManager a single collection with child-type ActionFigure from a normalization-of-behavior standpoint, but the data management behavior varries too much by ActionFigure type, I think, and when I get to validation it's different again (assuming I use the same classes for ActionFigure management). Even if it were one table or view, choosing which column from which to load data would vary by ActionFigure type, so its not really a database issue (do you agree?).
At any rate, one of my key goals for this approach would be that the ActionFigureManager should not need to be modified when we add a new ActionFigure type to the system; we should only need to implement the newest ActionFigure type class since that class would be responsible for loading and saving itself to its tables as well as replying to any ActionFigure Query. One approach I thought of to achieve this goal was in the ActionFigureManager to store a collection of type-specific, ActionFigureCollection-derived, editable-root-collection CSLA objects; polymorphism at work. The ActionFigureManager internally maintains a collection of ActionFigureCollections, but each entry is a different collection type for each type of ActionFigure, and each collection does its thing in its own way while deriving from a base ActionFigureCollection - or perhaps they all implement a certain IActionFigureCollection interface. Going back to the goal of not modifying the ActionFigureManager when a new ActionFigure type is added to the system: I'd like to avoid a switch statement in the ActionFigureManager over the ActionFigure type which would hard-code the ActionFigure type code from the DB (or a string or type name) to a specific collection class type and it's constructor. Reflection, I'm looking at you... It's somewhat akward, but along with the ActionFigure type identifier in the database (probably an int), I could add the associated strong class name and have the ActionFigureManager load the appropriate collection type via reflection, then work on it once its cast to the base ActionFigureCollection type. In other words, ActionFigureManager is a Factory that creates ActionFigureCollection-derived Factories for ActionFigure objects - or as Number 2 in Austin Powers put it: "A factory that manufactures miniature replicas of factories". Shark.FrickinLaserBeam.Fire().
Seems clever...maybe too clever. It does meet my criteria though: ActionFigureManager doesn't need to know about the different types of ActionFigureCollections, but can still retreive ActionFigure data and respond to ActionFigure Queries. I do know that generics (and hence CSLA objects) in C# are not polymorphic, but I believe setting up a root ActionFigures collection class from a CSLA template and implementing an "OnQuery", "OnFetch" methodology to provide hooks for derived classes should do the trick. Nyet?
The second, and less thouroughly thought out implementation, would be to use a UserHasActionFigureCommand CSLA Command object to simple query the database directly. This however puts the responsibility of firguring out which tables to join to on the SQL statement or stored procedure. I think it also only delays the problem, since this only addresses the ActionFigure Query, and not any data management tasks like modifying the user's ActionFigure collection info.
So, have any of you CSLA framework experts done anything similar to this? How have you handled the situation? How am I doing with my attempts? What suggestions do you have?
Thanks in advance, I'm looking forward to hearing your suggestions!
Hmm, looks like you have a Pattern in search of a problem.
I am assuming your object model is something like:
Action Figure: Id, Name, Manufacturer(Id), Release date, etc.
Action Figure Types: Star Wars, Star Trek, Battlestar Galactica, Transformers, Smirfs, etc.
Each type has additional properties specific to it, like Movie(Id), Season(Id), etc.
To me, it calls for a Composition Pattern with a static class like Assignment in PTracker with concrete classes for each type. You may have a ActionFigureList which has the common properties, but will also have Lists for each type to get a list based on the type specific properties (StarWarsActionFigureList.GetListByMovie('Episode I'))
You, in part, said:
I'm working with a list of objects that we'll call "ActionFigures".
This thread is not intended to discuss the merits of ActionFigures,
only their implementation.
In the system there are different types of ActionFigures. ActionFigures do many common properties, but each disctinct type also has it's own distinct properties and behaviors as well. ActionFigures are stored in one table in the database to provide a unique ID, but periphery tables join to the root ActionFigure table to hold any data that is unique to an ActionFigure type or classification. This is just to avoid a sparce table. What I need to do is be able to determine if a user has a given ActionFigure based on given parameters - we'll call this an ActionFigure Query.
-------
My preference in these discussions is to make sure I understand the use cases. I think that I detect at least two very distinct use cases: First, for a given user, determine if a particular type of action figure is associated with that user. The result of the inquiry is a boolean yes or no. Second, again for a given user, retrieve the data for a spedific action figure, if such an action figure is associated with that user.
For the first use case, I would probably create a command class that was smart enough to switch the query SQL based upon the action figure type. This is chatty but is probably more maintainable (and I worship regularly at the Shrine for Maintainability). One variation on this approach would be to pass the action type to a stored procedure that would handle the switch. Another variation on this approach would be to store the queries for each action figure type in a configuration file (or something similar); look for the action figure code in the list, use the associated query. At the extreme you could build a series of plug in assemblies that contained the code to perform the queries. Unless you have hard requirements that dictate that you be able to add a new action figure in a matter of minutes, I would stick with the initial command class. Rule of thumb: avoid cleverness unless the situation requires it (and really question if the situation really does require it).
I suspect that you are engaged in premature optimazation. I would argue that you should make sure that your design is decoupled as much as is reasonable. Only when you have hard data that shows that you have a performance problem would I consider such tactics as caching data. If I did cashe data, I would probably create a very minimal data structure: user identification and the identification codes of the action figure types associated with that user. This would be a read-only list/item setup. It has one purpose: to answer the question: is this user associated with s specified action figure type?
Now for the second use case: I would probably use the same command class approach with the difference that I would either hand back an instance of an action figure class or nothing/null. The same arguments for delaying the caching decision would apply here.
Your references to polymorphism suggests that there might be a third use case that would deal with a set of use cases. There is not enough material in your post to do anything with this use case.
There are probably several other use cases. One of the hallmarks of CSLA is that, in the absence of compelling reasons to the contrary, there ought to be a separate set of objects that handle each use case. My experience is that separation is the optimum approach: as the use cases change (and they do change), you gradually carve up the classes that you combined to "save coding" into separate classes that, in effect, build firewalls between the use cases.
HTH
Jon StonecashGreat suggestions guys! I really appreciate the input! Also, I appologize for the late reply, it seems the reply notification emails from the forum may be blocked by my email filter.
I defninitely agree that there needs to be a "type" column in the root ActionFigure table. The type can indicate which secondary table we need to address in our Sql join. One other approach we're looking at is adding a XML blob column to the root ActionFigure table for extended properties and eliminating the secondary tables by type...this should work since we aren't putting any foreign keys or other constraints on the values in the XML (that aren't going to be handled by the business objects).
SomeGuy, I'll definitely familiarize myself with the Composition Pattern and see how I can apply this to my ActionFigure problem.
As I've continued to work with this issue, I've really boiled the problem down to "How do I perform a search across non-homogeneous data using CSLA and SQL in an extensible way?" To answer your question, Jon, there is a requirement for being able to add ActionFigures quickly and easily - although not manually by the user's themselves at this point (ActionFigure is just a metephor, not our real objects). I can expand on it a bit though:
To extend the example, the ActionFigure is put into the product catalog by our staff, but it's added to the user's collection via shopping-cart, so we need to search a user's collection for an ActionFigure with unknown extended properties which have been defined by our staff. New definitions can be added at any time, and we'd like to not have to rewrite the search when the staff defines a new figure with new extended properties. To be clear, the extended properties which we are searching for will be known as part of the search, but we'd like the searching mechanism to support the extended properties genericly without actually having to understand their definition...sort of like SQL fulltext searching; it doesn't need to understand the spoken language of the text data, it only needs to understand Unicode.
As I'm writing this, and thanks to all your suggestions, I'm wondering if the XML blob column might be even more helpful than we thought. Going with a single Command object to return a boolean "Exists" result of a search, and combining that with Jon's suggestion of putting specific queries in the config file, perhaps I can write the portion of the SQL search that checks the extended properties as XPath and place that in the config. Then the system can look up the XPath by ActionFigure type and pass that parameter to the SQL via the Command object. With any luck, I may even be able to auto generate the XPath from the initial definition of the ActionFigure properties.
Any other thoughts or suggestions? Has anyone tried this approach?
You could implement the Extended Properties as a child collection of the Action Figures. ie:
SELECT id, name, description, manufacturer, ....
FROM ActionFigures af
LEFT JOIN ExtendedProperties ep ON ep.actionFigureId = af.id
WHERE ep.name = @extendedPropertyName AND ep.value = @extendedPropertyValue
That's a good suggestion as well, altough I've never been a fan of vertical property tables. I think that would be a great way to build the CSLA business objects though, if there are no validation rules associated with the extended properties.
Thanks for the suggestion!
Vertical property tables do have their drawbacks, but it seems like a clean way to implement your Extended Properties. You wouldn't have any code changes or even fancy search coding to do.
I am not a fan of dynamic SQL, or passing complex WHERE clauses at text or XML.
Copyright (c) Marimer LLC