1

Strategy in the Real World

by Jeremy 9. June 2011 16:13

I was recently working with a switch statement that was growing out of control, even as I was developing it.  In my opinion, if you have to scroll to see all cases of a switch statement, it's time to refactor.  So, I used this as an opportunity to apply the strategy pattern.

The switch statement looked like this when I started getting spaghetti-code nausea.  

switch (comparisonOperator)
{
   case Operator.IsEqualTo:
      return WhereFilter.Equal(property, whereCriteria.Value);
   case Operator.Contains:
      string likeExpression = string.Format("%{0}%", whereCriteria.Value);
      return WhereFilter.Like(property, likeExpression);
   case Operator.StartsWith:
      likeExpression = string.Format("{0}%", whereCriteria.Value);
      return WhereFilter.Like(property, likeExpression);
   case Operator.EndsWith:
      likeExpression = string.Format("%{0}", whereCriteria.Value);
      return WhereFilter.Like(property, likeExpression);
   case Operator.IsGreaterThan:
      switch (whereCriteria.ValueType)
         {
            case whereCriteria.ValueType.DateTime:
               return WhereFilter.GreaterThan(property, DateTime.Parse(whereCriteria.Value));
            default:
               throw new NotImplementedException();
         }
   case Operator.IsLessThan:
      switch (whereCriteria.ValueType)
      {
         case whereCriteria.ValueType.DateTime:
            return WhereFilter.LessThan(property, DateTime.Parse(whereCriteria.Value));
         default:
            throw new NotImplementedException();
      }
   //TODO: Implement all cases
   case Operator.IsGreaterThanOrEqualTo:
   case Operator.IsLessThanOrEqualTo:
   case Operator.IsNotEqualTo:
   case Operator.IsNotNull:
   case Operator.IsNull:
   default:
      throw new NotImplementedException();
}


As you can see, it's getting a bit out of hand and there are still quite a few cases (and even nested cases) that still need to be coded.  After I finished applying the pattern, the code in the corresponding class looked like this.

IOperatorStrategy operatorStrategy = OperatorStrategyFactory.GetStrategy(theOperator);
return operatorStrategy.GetWhere(property, criteria);


The OperatorStrategyFactory retrieves the appropriate strategy class based on the input parameter. 

internal static class OperatorStrategyFactory
{
   private static readonly IDictionary<OP, IOperatorStrategy> Strategies = new Dictionary<Operator, IOperatorStrategy>();

   static OperatorStrategyFactory()
   {
      Strategies.Add(Operator.IsEqualTo, new EqualToStrategy());
      Strategies.Add(Operator.Contains, new ContainsStrategy());             //These could be loaded via reflection as well, if beneficial
   }

   public static IOperatorStrategy GetStrategy(Operator @operator)
   {
      if (!Strategies.ContainsKey(@operator))
      {
         throw new NotImplementedException(string.Format("An operator strategy is not registered with the StrategyFactory for the operator '{0}'", @operator));
      }
      return Strategies[@operator];
   }
}


The returned strategy class knows the algorithm required to then supply the necessary result (i.e. where clause in this case). 

internal class EqualToStrategy : IOperatorStrategy
{
   public IWhere GetWhere(IQueryProperty property, Critera criteria)
   {
      //TODO: Implement Algorithm
      throw new NotImplementedException();
   }
}


This is a simple way to make your large switch statements more maintainable and keep the spaghetti-code sometimes associated with them to a minimum.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | Abstraction | Design Patterns | Software Maintainability

1

LINQ != LINQ to SQL

by Jeremy 16. September 2010 10:48

I've spoken with multiple developers who interpet "LINQ" as LINQ to SQL.  Yes, they are certainly related, but LINQ is not just LINQ to SQL.  LINQ to SQL is likely the most well known LINQ Provider, although its only a small part of the LINQ umbrella.  In fact, LINQ to SQL will likely fade into the sunset, as Microsoft will no longer be enhancing it, but rather focusing on the Entity Framework. 

LINQ provides an abstraction for querying data, regardless of where that data resides.  Consider the following line of code.

IQueryable<Comment> publicComments = comments.Where(x => x.IsPublic);


As a developer, I don't need to know where this data resides, or how it is retrieved.  It could be translated into SQL by LINQ to Entities, LINQ to NHibernate, or LINQ to LLBLGen.  It could be translated into CAML by LINQ to Sharepoint.  It could be translated into Twitter's API access code by LINQ to Twitter.  The bottom line is, as a developer consuming LINQ, I don't have to worry about it. If I am using a tested and proven LINQ provider, the internal details of that data access have already been hashed out by the provider developers. 

The number of LINQ providers continues to grow, and its well worth your time to learn the abstraction.  LINQ to SQL may fade away, but LINQ will not.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | Abstraction | C# | LINQ

3

ELMAH - a benefit to every asp.net website

by Jeremy 4. February 2010 11:33

ELMAH is an open-source .Net dll that allows for simple, pluggable exception notification and logging.  It takes all unhandled exceptions and allows the developer to configure notifications and/or logging of those exceptions.  There are many different configuration options, including where to log (in memory, database, text file, etc), and how to notify (email, twitter, etc.).  The configuration below assumes a common scenario of sending an email and logging to sql server when an unhandled exception occurs.

Steps for setup:

  1. Download the latest zip file (includes dll and documentation) http://code.google.com/p/elmah/
  2. Reference the dll from your website
  3. Run the included database script (located in the "db" folder of the downloaded zip) on the database where you would like to log exceptions
  4. Modify the config (assumes II6, see documentation on elmah site for IIS7)

<sectionGroup name="elmah">
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
    </sectionGroup>
    
    <httpHandlers>
      <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
    </httpHandlers>
    
    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
    </httpModules>

    <elmah>
        <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="configuredConnectionString" />
        <errorMail from="fromEmail" to="developerEmail" cc="ccEmail" subject="emailSubject" smtpServer="smtpServerIP" />
    </elmah>

 

See sample web.configs: http://elmah.googlecode.com/svn/tags/REL-1.0/samples/web.config 

How to view logged exceptions:
http://website/elmah.axd

How to throw a test exception:
http://website/elmah.axd/test

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | Exception Handling

0

Try/Catch vs Global Exception-handling

by Jeremy 5. January 2010 17:51

Another developer recently asked me when try/catch blocks should be used in asp.net applications.  In most apps I write, I try not to pepper these statements throughout the code, but rather deal with exception handling on a global level.  The less code you write, the less code you have to maintain.  In my opinion, there are generally only two reasons to use try/catch statements: Either 1) you need to perform some action if the try code fails, or 2) you want to provide a more user-friendly (i.e. maintenance-developer friendly) error message.  Beyond these two cases, all unhandled exceptions can be dealt with on a global application level.  

For global exception handling, I prefer to use the open-source project ELMAH, which provides logging, notification, and UI functionality by simply plugging it in.  ELMAH performs this magic by using an http module to catch unhandled exceptions, combined with an http handler to display those logs via a UI.  However, if you don't want to use Elmah (stop being stubborn - it isn't that hard), you can always just put your exception handling code within the Application_Error method in the global.asax.

As for the two cases where try/catches should be used:
1) You need to execute some code if something fails.  Transactions are a good example - if you have a workflow that requires multiple steps to be completed within a transaction, and the first step fails, you will want to use a try/catch to perform a rollback within the finally block.

try
{
    //Start transaction
    //Perform step one
    //Perform step two
    //Commit transaction
}
catch(Exception)
{
    throw;
}
finally
{
    //Roll back the transaction
}

 

2) You want to provide an "English" error message.  Often-times, .net exception messages are fairly generic and don't always tip the developer off as to why something failed.  In these cases, you can catch the message and throw a new one that provides a more readable message.  Note that when you do this, you should always pass the caught exception to the constructor of your new exception - this way you won't lose the stack trace.


try
{
    //Get data from a vendor web service
}
catch(SqlException ex)
{
    string errorMessage = "The vendor api is not responding.  Give them hell.";
    throw new ServiceException(errorMessage, ex);
}

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | Software Maintainability | Exception Handling

1

Un-LINQ Stored Procedures

by Jeremy 18. November 2009 15:13

About a year or so ago, I read some developer opinions against the usage of stored procedures.  Jeremy Miller has several blog posts on the topic and Dino Esposito writes about the topic in his enterprise architecture book.  At the time, every application I had ever worked on had utilized stored procedures, so I was a bit perplexed as to how complex data access could be accomplished without them.

On a recent project, I decided to explore the concept that the database should be used for storage only.  I saw some validity in the idea that business logic should reside in the code, not the database (as stored procedures).  The application to which I'm referring is built upon Entity Framework, and utilizes Linq-to-entities for data access.

For simple, straight-forward queries like retrieving a set of journal entries created by a user, you can simply use something like the following:

var query = from log in Db.LogEntry where log.UserId.Equals(1) select log;

However, what about complex logic, like searching these logs while allowing the user to supply and/or choose from a long list of criteria?  Consider the following method signature:

public virtual IEnumerable<Logs> SearchLogs(int? userId, DateTime? beginDateIsAfter, DateTime? endDateIsBefore, bool? hasAttachments)
{
}


You need to search the table by not only the user id, but also a several other parameters that may or may not be provided.  Linq supports this (fluently) through the use of the extension method syntax and method chaining, as seen below.

public virtual IEnumerable<Logs> SearchLogs(int? userId, DateTime? beginDateIsAfter, DateTime? endDateIsBefore, bool? hasAttachments)
{
    var query = Db.LogEntry.Where(log => log.UserId.Equals(1));
    if (beginDateIsAfter.HasValue)
    {
        query = query.Where(x => x.BeginDate >= beginDateIsAfter);
    }
    if (endDateIsBefore.HasValue)
    {
        query = query.Where(x => x.EndDate <= endDateIsBefore);
    }
    if(hasAttachments.HasValue)
    {
        query = query.Where(x => x.HasAttachments == hasAttachments.Value);
    }
    return query.AsEnumerable();
}


You can chain on as many "Where" extensions as you please, as well as "OrderBy" extensions.  I have yet to encounter a sql query that could not be accomplished in Linq to Entities (granted, some require some mind-bending code).

My biggest concern regarding this usage of linq was the performance.  How many queries does this code execute and how efficient can that really be?  Fortunately, due to Linq's deferred execution, no sql is executed until the last possible minute.  So, in the case above, no sql is executed until the query is enumerated (the last line of the method), and only a single, parameterized query is issued.

As a result of utilizing linq and and its built in method-chaining capabilities, all of the if/then logic that would have been in a stored procedure fits nicely within the code.

Currently rated 3.0 by 1 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | C# | LINQ

Powered by BlogEngine.NET 1.4.5.0
Original Design by Laptop Geek, Adapted by onesoft