5

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?

Christofer Eliasson
  • 32,939
  • 7
  • 74
  • 103
Paul Perrick
  • 496
  • 9
  • 22

5 Answers5

8

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • How does this follow the open/closed principle? If I read Paul's post correctly he wants to avoid having to change existing classes (extending the switch statement with new cases). – Wivani Dec 21 '11 at 14:55
  • @Wivani: I didn't see anything suggesting that - I only saw that he wants the code to be simpler and easier to maintain. Where in the question does it talk about avoiding having to change existing classes? – Jon Skeet Dec 21 '11 at 14:56
  • Guess I'm making 'an ass out of u and me' ;-) Let's see if Paul can confirm what I think he is looking for. – Wivani Dec 21 '11 at 15:05
  • I'm willing to make any changes to the code which will make it better. So far I like the dictionary idea. My issue what that I've been in this situation possibly hundreds of times in the past 20 years. I now automatically write a switch statement and I was looking at it scratching my head and wondered if there is a better way. – Paul Perrick Dec 22 '11 at 03:27
  • Also, in the database I chose to store a key for each email template to make it easy to track the objects and debug. You can just look at the key and know which template is being run. I know an enum may have been a smarter solution, but this works also. – Paul Perrick Dec 22 '11 at 03:55
3

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

Lindan
  • 110
  • 9
1

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();
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • I was thinking of using a Factory, but then in the factory you end up with the same issue. You need to wire the requested template to the proper Query class. – Paul Perrick Dec 22 '11 at 03:29
0

Why not using reflection as you proposed in your question? I think its a valid way of doing this kind of stuff.

Another approach would be to use the inversion of control / dependency injection pattern. You define an interface as you did and register all the known concrete implementations to your DI container (this can be done by configuration or by code).

When registering, you need to tell the DI container some service name to distinguish the implementations, because they implement the same interface.

YourIocContainer.Register<IEmailQuery>(typeof(SomethingExpiredMailQuery),
                                       "SomethingExpiredMailQuery");

When instantiating, you can get the corresponding implementation by supplying the service name again:

public List<Membership> Execute(string TemplateKey) {
   YourIocContainer.Resolve<IEmailQuery>(TemplateKey);
Jan
  • 15,802
  • 5
  • 35
  • 59
  • When I was thinking of asking the question on stackoverflow the first thing I thought of was reflection. Speed is not an issue so it would be a valid method to use. I was just curious what other solutions may be out there I hadn't thought of. I do like your dependency injection solution also. – Paul Perrick Dec 22 '11 at 04:12
0

The command pattern is a perfect pattern to use for this scenario. See http://www.codeproject.com/KB/books/DesignPatterns.aspx for a practice c# description of this pattern. Lambdas as Jon Skeet has described are useful newer programming constructs that you can see. See Command Pattern : How to pass parameters to a command? for more discussion on the use of the pattern.

Community
  • 1
  • 1
Shan Plourde
  • 8,528
  • 2
  • 29
  • 42
  • Thank you for mentioning the Command Pattern. Forced me to reread the definition from the 4gang book. I'm curious though as to what I would gain by it's use. Wouldn't I still need to create an instance of the receiver to invoke the command? Brings me back to a list of classes and associating a requested command to the correct one. – Paul Perrick Dec 22 '11 at 04:08