Blog Stats
  • Posts - 15
  • Articles - 2
  • Comments - 22
  • Trackbacks - 19

 

Time for my yearly blog post

Well its been a great year. I'm thinking of moving on from my current contract - been 4 years slogging on at the same area. Longest contract I've ever had - heck, longest job I've ever had.

Well, on to more interesting things - like code. We have developed a unified Framework here where I am contracting at to centralize (aka reuse) more commonly used areas of functionality, mostly data access. All data access has been abstracted into static method calls such as (note class names have been changed):

DataSet myData = Framework.Data.Enterprise.EmployeeGateway.GetAllEmployees();

or, if a Business Object wrapper was available, can be:

EmployeeCollection myEmployeeCollection = Framework.Data.Enterprise.BusinessObject.EmployeeGateway.GetAllEmployees();

An application that wants employee data simply calls one of the methods above. Simple, huh? Now, we wanted to deploy the application behind a firewall. We no longer had direct access to the database and all data requests had to know be funneled through web services. But who wanted to recode 398 stored procedures? Using a bit of reflection, I was able to potentially open up all of the static method calls so that an application running outside the firewall was able to make the same database call without knowing it was actually been funnelled through one (or more!) web services for its data. Code tidbit below:

public class EmployeeGateway
{
  private EmployeeGateway() { }
  public static DataSet GetAllEmployees()
  {
    return ExecDataSetQuery(“GET_ALL_EMPLOYEES“, null);
  }
  private static DataSet ExecDataSetQuery(string sProc, SqlParameter[] parms)
  {
    // Connection factory returns either a SqlConnection or a WebService instance
    // based on a Framework configuration file. Connection information for a given
    // data source was either an encrypted connection string or an encrypted web
    // service endpoint.
    object connection = ConnectionFactory.CreateConnection(DataSourceEnum.Employee);
    DataSet ds = null;
    if (connection.GetType().Equals(typeof(SqlConnection))
    {
      using (SqlConnection sqlConnection = connection as SqlConnection)
      {
        // standard SqlDataAdapter / Fill code here
      }

    }
    else if (connection.GetType().Equals(typeof(Framework.Web.Services.Enterprise.EmployeeGateway)))
    {
      using (Framework.Web.Services.Enterprise.EmployeeGateway webServiceConnection = connection as Framework.Web.Services.Enterprise.EmployeeGateway)
      {
        try
        {
          MethodInfo method = webServiceConnection.GetType().GetMethod(ExtractWebMethodName(sProc);
          if (null != method)
            ds = (DataSet)method.Invoke(webServiceConnection, Framework.Data.SqlController.ToObjectArray(parms));
          else
            throw new Framework.Data.DataException(String.Format(“Unable to locate Web Service name of {0} on the {1} connection.“, sProc, DataSourceEnum.Employee.ToString()));
        }
        catch (System.Exception ex)
        {
          // publish error to a central event log
          throw new Framework.Data.DataException(String.Format(“Failed to execute Web Service {0}.“, webServiceConnection.ToString()), ex);
        }

      }
    }
    else
      throw new Framework.Data.DataException(String.Format(“Unhandled connection request {0} returned from ConnectionFactory.“, connection.ToString());

  }
}

The caveats of course is that the Web Service proxy names need to match the stored procedure names. Not a bad tradeoff if you don't have to write all the web service proxy instantiations in your code and get them for free. The bolded function names above are included below for completeness.

internal static string ExtractWebMethodName(string sProc)
{
  string webMethodName = sProc;
  int sProcSeperatorIndex = sProc.LastIndexOf(“.“);
  if (sProcSeperatorIndex > -1)
    webMethodName = sProc.SubString(sProcSeperatorIndex+1, sProc.Length - sProcSeperatorIndex);
  return webMethodName;
}

internal static Object[] ToObjectArray(SqlParameter[] parms)
{
  int parmLength = 0;
  if (null != parms)
  {
    // figure out array length
    foreach (SqlParameter parm in parms)
    {
      if (parm.Direction != ParameterDirection.Output)
        parmLength++;
    }
  }
  int index = 0;
  object[] args = new object[parmLength];
  if (null != parms)
  {
    foreach (SqlParameter parm in parms)
    {
      if (parm.Direction != ParameterDirection.Output)
        args[index++] = parm.Value;
    }
  }
  return args;

}


Feedback

# re: Time for my yearly blog post

Gravatar Perhaps a more appropriate design would have eschewed the static methods at component boundaries, and used a strategy pattern that would allow you to plug in either the web service strategy or the SQL connection strategy.

This would have made data access test code much cleaner and would ultimately have enabled you to tease out the data access from the call chain when testing business logic that ultimately calls into data access code (ie: mocking the data access strategy).

You could use Spring .NET to initialize your data access context instance with the correct data access strategy without having to write any config handling code. Configuration of deployed clients would simply be a matter of copying the appropriate config file.
9/22/2005 6:39 PM | Scott Bellware

# re: Time for my yearly blog post

Gravatar Thanks for your comments, Scott.

A question - aren't I using the strategy pattern already when I create my data connection in my controller class? There is a comment in the code that is a placeholder to a static factory class that either produces a SqlConnection or WebService instance. Are you further suggesting that the ExecDataSetQuery be pulled into another class and abstract it further?

Another thing that is not expressed in this code is that the connections themselves are defined in a framework configuration file. So that a deployed .dll can automatically change from direct database connection to web services calls simply by changing the connection definition in the config.

I didn't thing people actually read this stuff! =)

The Spring .NET point has got me intrigued. I guess I have some more reading/learning to do! Again, thanks for your comments, Scott. 9/26/2005 5:58 AM | Allen Guest

# re: Time for my yearly blog post

Gravatar Thanks GetType().Equals property helped me. 12/1/2008 9:00 AM | Neeraj

Post a comment





 

 

 

Copyright © Allen Guest