In an earlier blog, we discussed the need to reduce complexity and some of the problems that complex code can cause.  We even discussed a reliable quantifiable metric for evaluating complexity.

For review, we want to use the CodeMetrics add-in to Reflector to track Cyclomatic Complexity and target methods that have a complexity greater than 10.

One common source for elevated complexity is leaving artifacts from the data access in the business logic.   We are probably all familiar with code that looks like this:

  IDataReader dr;
    try
    {
        dr = DAL.GetSomeData(filter);                                                                                
        while (dr.Read())
        {
          if ( dr["field"] != System.DBNull.Value)
            {
                DateTime convertedDate = Convert.ToDateTime(dr["field"].ToString());
            }

         if ( dr["nextField"] != System.DBNull.Value)
            {
                Double convertedDouble = Convert.ToDouble(dr["nextField"].ToString());
            }
              . . . .

  catch (Exception ex)
    {
        // Handle exception
    }
    finally
    {
        dr = null;
    }
}

Here we have a common scenario of reading data from a data reader and verifying that the columns are not null.   If they are not null, we convert the field to a strongly typed variable and then proceeded to implement business logic with the strongly typed variables.   As you can see from the above code fragment, we are dealing with two fields from the database and we already have a complexity of at least 6.   There is a direct correlation between the number of fields being processed and the complexity of the method.   We cannot have a linear relationship between complexity and the number of fields that we are referencing and expect to keep complexity below 10.   A better approach is needed.

A better way might mean adding a function similar to this:

        public object RetrieveDataFromDataReader(IDataReader dr, Type dataType, string whichField)
        {
            if (dr[whichField] == System.DBNull.Value)
            {
                return GetNullRepresentationValue(dataType);
            }
            else
            {
                return Convert.ChangeType(dr[whichField], dataType);
            }

        }

Armed with such a method, we can now rewrite our original method tobe something like:

  IDataReader dr;
    try
    {
        dr = DAL.GetSomeData(filter);                                                                                
        while (dr.Read())
        {
            DateTime convertedDate = (DateTime) RetrieveDataFromDataReader (dr, typeof (DateTime), “field”);
            Double convertedDouble = (Double)  RetrieveDataFromDataReader (dr, typeof (Double), “nextField”);
           
              . . . .

         }

   }

  catch (Exception ex)
    {
        // Handle exception
    }
    finally
    {
        dr = null;
    }
}

This new version should have a constant complexity regardless of the number of fields introduced.   This is a great improvement.

Now, there most likely is still business logic embedded in the data access logic.  This is also bad, but it is not a problem made apparent through a strict review of code complexity metrics.

Next time, we will review why it is bad to merge the data access logic and business logic and review some easy ways to tease apart these interdependencies.

posted on Wednesday, January 09, 2008 9:03 PM |
  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Comments

No comments posted yet.
Post Comment
Title *
Name *
Email
Url
Comment *