This question has probably been posted before, but I couldn't find it.
I've been writing this sort of thing for so long, I sit down to write something new and just start typing this as if it were my own pattern. A project came up recently and I found myself looking at my own code and started thinking how smelly it looks.
 BackgroundInfoIfYouCare
In this particular library I need to send out emails to users. So far there are 13 canned emails.
Each email has it's own template (I'm using a Razor parser, so the templates are written in cshtml). Each email template has a Name Key of string. Each email has it's own EF4 query to return a model based on a "membership" entity and all related data.
I have a class that accepts a string which is a Email Template Name Key.
The method will run the appropriate query and get back a list, grabs the email template.
The list and template are passed to a parser to merge each of the memberships to the template and returns a list emails.
 EndOfBackgroundInfoIfYouCare
So the real question... what is the best way to do this?
One way is to just use a switch
public List<Membership> Execute(string TemplateKey) {
switch (TemplateKey) 
        {
            case "SomethingExpired":
                QueryResult = new SomethingExpiredEmailQuery().ExecuteQuery();
                break;
            case "SomethingExpireIn30":
                QueryResult = new SomethingExpireIn30EmailQuery().ExecuteQuery();
                break;
            case "FirstTimeLoginThanks":
                QueryResult = new FirstTimeLoginThanksEmailQuery().ExecuteQuery();
                break;
            case "SecurityTraining":
                QueryResult = new SecurityTrainingEmailQuery().ExecuteQuery();
                break;
            case ETC ETC ETC...
}
Another way would be to use an interface
IEmailQuery
void ExecuteQuery()
But if I use an interface I will still need to instantiate the Query class. It saves no code and does not make the code easier to maintain.
With reflection I could do something like name all of the Email queries with a pattern: Email Template Key of SecurityTraining has a query name of SecurityTrainingEmailQuery and I could use reflection to instantiate and call the ExecuteQuery method.
Without using reflection, is there no cleaner way of wiring this up?
One option is to have a Dictionary<string, Func<IEmailQuery>> map. You could build it like this:
private static readonly Dictionary<string, Func<IEmailQuery>> MailQueryMap = 
    new Dictionary<string, Func<IEmailQuery>> {
    { "SomethingExpired", () => new SomethingExpiredMailQuery() },
    { "SomethingExpireIn30", () => new SomethingExpireIn30EmailQuery() },
    // etc
};
Then:
public List<Membership> Execute(string templateKey) {
    IEmailQuery query = MailQueryMap[templateKey].Invoke();
    var queryResult = query.ExecuteQuery();
    // ...
}
If you can guarantee that you only ever need parameterless constructors, you could always store a Dictionary<string, Type> and instantiate it via reflection - but there will be some ugly casts etc.
EDIT: Of course, if the name of the template always is the name of the type, you could use
Type queryType = Type.GetType(namespacePrefix + "." + templateKey);
IEmailQuery query = (IEmailQuery) Activator.CreateInstance(queryType);
var queryResult = query.ExecuteQuery();
You may also want to consider using an enum instead of the magic string constants.
Actually, this doesn't look too smelly to me. If you don't like the switch-statement you could go the IEmailQuery-Path and just wire it up in a Dictionary<string,IEmailQuery> .
This probably saves some lines of code, as you could access it like that:
QueryDictionary["MyKey"].ExecuteQuery(); 
Cheers, Oliver
I'd go for a Factory pattern, something like
class EmailQueryFactory
{
  public IEmailQuery Create(String TemplateKey)
  {
    ....
  }
}
and then
//.. first get String TemplateKey
IEmailQuery qry=EmailQueryFactory.Create(TemplateKey);
qry.Execute();
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With