Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

A little background on where this question is coming from. In my current .NET application I'm working on some features related to archiving a certain type of business entity called a "Project". Depending on the situation what has to be archived, and the exact actions involved in archiving can vary wildly. My basic idea is to create an entity called an ArchiveSchedule which basically stores data saying this kind of project should be archived after this amount of time.

I want to encapsulate my queries for selecting all the objects to archive and the commands for actually archiving off into their own objects that inherit from some common interfaces. There will be multiple archive queries(select items based on various properties, or whether they've been archived before and later restored, etc), and multiple archive commands(ie commands that send data to different interfaces, some that perform deletes, etc.). What I'm considering doing is including on the ArchiveSchedule, references to the actual .NET type corresponding to the query and command I want to use for this schedule. Then when I want to actually run the queries and commands I use reflection to build up the object and execute the command or query.

Is this basic strategy of storing type information in the database and then using reflection to create the objects and inject dependencies an antipattern? Or is there a better way to do it?

Note:I know there are some issues with just storing the type name or namespace, because those might change during refactoring, but I believe those could be mitigated fairly easily using a GuidAttribute on the class definition and storing the guid instead, so I'm curious if there are more fundamental issues beyond that.

share|improve this question
1  
If you're using SQL Server, you may want to look into XML storage type with or without schema support. –  JeffO Apr 12 '13 at 16:43
    
This is one of those cases where you can get around the need for reflection by using the strategy pattern. See my answer. –  Mike Brown Apr 12 '13 at 18:16
    
I agree with JeffO on this, but wish to note that you this is only effective if none of the object members need to be indexed. For example, a username should not be stored in an xml object, since this will make lookups by username very slow. On the other hand, a user's personal configuration settings might be fine in XML, since normally you would not use such settings to look something up (though a query like, all users with setting X turned on will be very slow, as it will mean parsing XML for every user). –  Brian Apr 12 '13 at 18:34

4 Answers 4

up vote 4 down vote accepted

Yes, generally. You're then binding the type name to outside data, limiting your refactoring capabilities and making people change stuff in two places when they make a new class. Further, you're (or at least someone is...) going to be a world of hurt in ~10 years when C# becomes out of date. New implementations might not have the same classname, let alone idioms for instantiation.

I would tend towards not including this in the database, and even if you did, storing some implementation agnostic form of the command. That might be more work, but is a bit more flexible and robust design overall.

share|improve this answer
    
So store a string and then make a class responsible for resolving strings to classes? I think I like that alot. Particularly because I could start out with a simple resolver just using switch statements and eventually change it to something that just uses convention based mapping based in some form or fashion. –  AndrewSwerlick Apr 12 '13 at 17:41
    
@AndrewSwerlick - I wouldn't store the string in the database, a simple value should be sufficient (with perhaps a lookup table to map the values to some human understandable form). –  Telastyn Apr 12 '13 at 18:08

You are overthinking your problem. Most O/RM's support what's called Object Hierarchy Mapping. Using the Strategy Pattern, you can encapsulate the logic for archiving your Project into a ProjectArchiver hierarchy. Provide an Id for the Archiver and map it to the database.

The base class should have a virtual Archive() function, that all the child classes implement.

When the object is retrieved from the database, it will create an instance of the appropriate subclass with the correct implementation of Archive. And all you have to do is call it.

share|improve this answer
    
For this specific scenario I think your approach makes sense. When I created the initial design I was thinking that inheritance wasn't the best plan because I might want to mix-and-match the query and command portions of the archiving, and that could be hard to do without a messy multi-level inheritance setup. As I'm examining the requirements closer though, I'm realizing the mix and match is overkill. I'm still going to accept the earlier answer though because it better answers the more generalized question that I thought I needed an answer too. –  AndrewSwerlick Apr 12 '13 at 18:54
    
In the end, it's somewhat doing the same thing, but the framework handles it all for you ;) –  Mike Brown Apr 12 '13 at 18:59

A reflection system like this can be really brittle and it's not always clear why it's failing when it does fail.

It obviously fails when the type name isn't correct but there's no indication of why or how it's incorrect. For example, you could simply not have the appropriate assembly reference and then it'll fail. You can infer that based on having a proper type name (it works somewhere else when you do Type.GetType(typename)), but that's quite a bit of testing and poking around you have to do compared to other classes of bugs. In order to find the Guid, you'll have to search the appropriate assemblies which only partially mitigates the surface area for problems.

Your problem also isn't specific to the database, but any time you attribute type information to a persistent source. One issue is how you'll deal with changing properties (removing or adding) and other changes like that.

Another potential issue is performance, reflection is relatively slow but it's not likely to matter unless you're using it in a way that you probably shouldn't be.

Overall, I don't think I would call this type of thing an antipattern. There are DBs that are designed for object orientation which would be ideal here but then you'd have two DBs to maintain if the rest of your application fits better in a relational context. It can really save development time and reduce errors if used properly, but it can also cost a lot of development time and increase errors if this code is going to be modified a lot or the people consuming the code don't really understand it. It comes down to using tools properly.

share|improve this answer

This sounds like a possible candidate for a document database like Raven or MongoDB rather than a relational database.

With document databases you are not locked into a schema, you can store anything of any structure which could fit more with your data in that you don't necessarily know exactly what you want to store up front.

share|improve this answer
1  
This might be a great answer, but it lacks the details necessary to make clear whether it is or not.. Please add some details (I didn't think of this but it does sound right... convince me) –  Jimmy Hoffa Apr 12 '13 at 15:36
    
@JimmyHoffa - added a bit more info, I'm no expert in document DB's however :) –  jmo21 Apr 12 '13 at 15:50
    
I'm not positive that this solves the problem because I can envision asking the same question with a document DB. Ultimately the question is how do you persist the relationship between a simple object and a complex algorithm embodied in code. Document databases may be schema-less, but ultimately they just store data, not algorithms. –  AndrewSwerlick Apr 12 '13 at 17:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.