Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Environment Tests and Self-Diagnosing Configuration (with StructureMap)

I added an exciting new feature to our suite of applications this week.  Not "seeing Star Wars for the first time" exciting or maybe even Ajax/Web 2.0 cool, but something that will definitely make our collective lives easier.  I added environment tests to our codebase and deployment scripts to troubleshoot and automate the verification of the deployed build.  The deployment script is now self-diagnosing.  Even better yet we could take advantage of the diagnostic tools in my StructureMap tool to embed the environment tests directly into the code since we already use StructureMap for configuration and attaching dependencies.

Our main application isn't that big, but it's nasty to deploy because it has a lot of environmental dependencies and configuration, but not much visibility into exceptions and failure conditions.  When we inherited the application we spent a lot of manhours getting the application to run correctly on our developer workstations.  We had to literally spend a couple man-weeks to make a new development environment work correctly.  In the last release we had at least two instances of a botched testing deployment costing us a day to track down and wasting the time of scarce testing resources.  Environment tests to the rescue. 

The new environment tests paid for themselves on day one.  I immediately found two assemblies and a certificate file that was missing from the deployment scripts.  We would have found the problems before rollout anyway, but the environment tests told me exactly where the problems were, and that's exactly what we wanted.

Here are some of this things we've created environment tests for so far:

  • Database connectivity
  • Remoting configuration
  • Web service availability, including security configuration
  • External files
  • Our custom rules engine component stores client configuration data in xml files.  These configuration files can contain references to client specific database tables and stored procedures.  We have an environment test now that verifies the existence of the configuration file for each client in the database and checks that the referenced database objects actually exist
  • Any and all dependencies on legacy COM objects.  Nothing aggravates me more than troubleshooting whether or not a COM component is properly registered.  Death to COM!
  • NHibernate mappings and configuration
  • File and assembly manifest.  I've put a new set of NAnt tasks in StructureMap to create and check a list of versioned .Net assemblies and other files (compares a hash of the file contents) in a deployed directory.

At some point we'll supplement the StructureMap environment tests by integrating some sort of synthetic transaction to run fake messages all the way through the system.

I first envisioned the environment and configuration troubleshooting in StructureMap after a nasty incident on a project two years ago.  We were building a WinForms client that talked to web services.  One day in the standup meeting a tester got up and said the testing installation wasn't functioning and all testing was stopped.  With the project manager fuming right behind me, I grabbed the current version of the client in testing and spent the better part of the day banging my head against the problem.  Inevitably, it worked perfectly on my box, but blew up with a seemingly randomly selected http error code as soon as I tried it on a tester's workstation.  Eventually I checked the configuration for the client and realized the testing client was trying to access the web services at "localhost."  Perfectly valid on any developer's workstation, but invalid anywhere else.   Several man days went down the drain on an already challenged project.

I think I could have easily filled up this entire post with anecdotes of bugs caused by incorrect or incomplete testing deployments and 48 hour marathon production moves, but I think you get the point (and you've probably lived through it anyway).  Incomplete, invalid , or just plain wrong code deployments can be a major source of inefficiency.  My theory was that I could embed the ability into StructureMap to make a codebase be self-diagnosing to "tell" you about any problems or inconsistencies immediately after a deployment.  In other words, make the code tell you exactly when and where there's a problem in the deployment before a tester wastes anytime with a bad build.  Not to mention making the 48 hour, let's all camp out in the warroom, production moves much less risky and stressful.

How StructureMap Validates a Deployment

First off, to really take advantage of StructureMap's abilities you need to very consciously build a system somewhat as a microkernal.  The functional core of the system (business logic, user interface controlling logic, workflow logic) needs to be isolated from external dependencies and services like data access/persistence, middleware, web services, and even just external files.  The core of the system should only depend on abstracted interfaces to the external dependencies.  The external dependencies are managed and attached via Dependency Injection and/or Service Location by StructureMap (or Spring.Net, Castle, etc.).  Now that StructureMap is aware of all of the external dependencies in our system, we can take advantage of the built in troubleshooting tools within StructureMap to embed environment tests directly into the classes controlled by StructureMap.

Instead of code like this that creates dependencies with a call to "new" and "pulling" configuration:

    public class DataProvider
    {
        private string _connectionString;
 
        public DataProvider()
        {
            _connectionString = System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"];
        }
    }
 
    public class Worker
    {
        private DataProvider _provider;
 
        public Worker()
        {
            // Strongly coupled dependency on the DataProvider class that pulls its configuration
            // out of the App config file
            _provider = new DataProvider();
        }
    }

use StructureMap to find the external dependencies and "push" in the configuration.

    public interface IDataProvider{}
 
    public class DataProvider2 : IDataProvider
    {
        private string _connectionString;
 
        public DataProvider2(string connectionString)
        {
            _connectionString = connectionString;
        }
 
        // Decorate this method with the ValidationMethod to instruct StructureMap
        // to call this during diagnostics
        [ValidationMethod]
        public void ValidateConnection()
        {
            // try to open a database connection
        }
    }
 
    public class Worker2
    {
        private DataProvider _provider;
 
        public Worker2()
        {
            // Pull the dependency from StructureMap, and give StructureMap a chance to call
            // any validation methods
            _provider = (DataProvider) ObjectFactory.GetInstance(typeof(IDataProvider));
        }
    }

StructureMap has quite a bit of diagnostics support that can be used from either a command line tool, a NAnt task, or a GUI tool that provides a Windows Explorer-like view of the configuration (in the forthcoming version, it'll be released on SourceForge only after we dogfood it a while longer).  So, here's some of the things StructureMap will do to diagnose a deployment:

  1. Look for any referenced assemblies that might be missing
  2. Check that all referenced concrete classes can be created within the deployment
  3. Verify that all required arguments to either constructor functions or setter properties exist with a valid value.  I.e., you can't put "hello" into an integer argument.  Because StructureMap depends on constructor arguments and setter properties, it's able to use reflection to determine the properties that need to be present and configured.  If you add a new constructor argument to a concrete class that is configured by StructureMap, the diagnostics will complain if you forget to go add a value for the new argument to configuration.
  4. Try to create each and every configured instance in the StructureMap configuration.  Reports any failures.
  5. Lastly, search the objects that were created successfully for methods decorated with a [ValidationMethod] attribute.  Call these methods to give the classes a chance to perform environment tests or further validation.  Report any exceptions thrown.

A while back I had some comments asking why or how StructureMap is better or at least differnt than the other DI tools for .Net.  I'd claim that the self-diagnostic configuration and environment tests are a big advantage over the other tools.

Example

An obvious example for an environment test is database connectivity.  The average enterprise application is an inert lump without being able to connect to the database.  When we do traditional data access we use a class called DataSession as our facade to ADO.Net.  The very first environment test I added to our code was the ValidateConnection() method in the DataSession class that is shown below:

    [Pluggable("Default")]
    public class DataSession : IDataSession
    {
        private readonly IDatabaseEngine _database;
        private readonly ICommandFactory _factory;
        private readonly IExecutionState _defaultState;
        private readonly ITransactionalExecutionState _transactionalState;
        private IExecutionState _currentState;
        private readonly ICommandCollection _commands;
        private readonly ReaderSourceCollection _readerSources;
 
        [DefaultConstructor]
        public DataSession(IDatabaseEngine database)
            : this(database, 
                   new CommandFactory(database),
                   new AutoCommitExecutionState(database.GetConnection(), database.GetDataAdapter()),
                   new TransactionalExecutionState(database.GetConnection(), database.GetDataAdapter()))
        {
 
        }
 
 
        [ValidationMethod]
        public void ValidateConnection()
        {
            using (IDbConnection connection = _database.GetConnection())
            {
                connection.Open();
            }
        }
 
    }

In the NAnt deployment script there is a call to StructureMap's task to validate the configuration like this:

<structuremap.verification configPath="${deployment.dir}\StructureMap.config" failonerror="${fail.on.environment.tests}"/>

This NAnt task directs StructureMap to analyze and validate the configuration of the .Net application deployed to the "${deployment.dir}," including a call to the DataSession.ValidateConnection() method.  If the DataSession instance is either missing the configuration for the connection string, or the connection to the database fails, the build fails.  If the verification task finds any errors, the build fails with a report of the specific problems.  Here's a sample output from the verification task I created by when the database was offline.

StructureMap.Configuration.Tokens.InstanceToken
     PluginFamily:  StructureMap.DataAccess.IDataSession, StructureMap.DataAccess (Explicit)
DefaultKey ShareDoc
          StructureMap.Configuration.PluginGraphReport



Problem:  A Validation Method Failed

SQL Server does not exist or access denied.
   at System.Data.SqlClient.SqlInternalConnection.OpenAndLogin()
   at System.Data.SqlClient.SqlInternalConnection..ctor(SqlConnection connection, SqlConnectionString connectionOptions)
   at System.Data.SqlClient.SqlConnection.Open()
   at StructureMap.DataAccess.DataSession.ValidateConnection() in 
d:\work\sideshow-trunk\source\StructureMap.DataAccess\DataSession.cs:line 175
--------------------------------------------------

The big advantage of doing environment tests with StructureMap is the ability to accrue the environment tests as new external environment dependencies are added to the codebase instead of a big bang approach at the end.  All you have to do is put methods directly in the environmentally sensitive classes that will throw exceptions if the environment is incorrect.  I really like having the environment tests embedded directly into the code because it means fewer pieces to deploy. 

This functionality is available in the version of StructureMap released on SourceForge, but it is much more robust in the forthcoming version that we're using at work.  I will make another release in the near future.

Links


Posted Thu, Apr 6 2006 9:35 AM by Jeremy D. Miller

[Advertisement]

Comments

Gary Williams wrote re: Environment Tests and Self-Diagnosing Configuration (with StructureMap)
on Thu, Apr 6 2006 11:25 AM
Thank you for the prompt response to my request.  But shouldn't

"The average enterprise application is an inert lump without being able to connect to the database."

be

"The average enterprise application fails fast when not able to access its persistence layer?."

Just kidding.  :)
Brad Jones wrote re: Environment Tests and Self-Diagnosing Configuration (with StructureMap)
on Thu, Apr 6 2006 11:32 AM
Don't mean to sound pushy, but how soon is "the near future"?
Jeremy D. Miller wrote re: Environment Tests and Self-Diagnosing Configuration (with StructureMap)
on Fri, Apr 7 2006 8:17 AM
Brad,

"The near future" is looking like early May.  I need to update or write a bit of documentation and improve the usability of this stuff a bit first.

Jeremy
Jeremy D. Miller -- The Shade Tree Developer wrote Finishing Off Each Iteration with a Deployable Product
on Mon, Apr 17 2006 9:08 PM
In my last post I talked a little bit about the difficulties that my team is facing in driving the user...
Jeremy D. Miller -- The Shade Tree Developer wrote Lessons Learned for Dealing with Legacy Code
on Thu, May 4 2006 11:16 AM
Tip #1 - Go get a copy of Michael Feather's &quot;Working Effectively with Legacy Code&quot; book.&amp;nbsp; If you...
Jeremy D. Miller -- The Shade Tree Developer wrote Blog appearance update
on Sun, May 7 2006 4:41 PM
Thanks to John Ver Voorn, Ben Scheirman, and Josh Twist for some help on this.&amp;nbsp; Right now it looks...
Jeremy D. Miller -- The Shade Tree Developer wrote Best of the Shade Tree Developer (with actual links!)
on Mon, Aug 7 2006 4:51 PM
Between being extremely short handed at work, tech' reviewing a new book, a
possible book proposal...
Jeremy D. Miller -- The Shade Tree Developer wrote Best of the Shade Tree Developer (with actual links!)
on Fri, Sep 1 2006 2:33 PM

Between being extremely short handed at work, tech&#39; reviewing a new book, a possible book proposal

Jeremy D. Miller -- The Shade Tree Developer wrote Removing the "Legacy" from your Code
on Sun, Apr 1 2007 8:07 AM

In a conversation last week I was asked for my recommendations on how to retrofit automated testing and

Jeremy D. Miller -- The Shade Tree Developer wrote Dependency Injection still has some good years left...
on Fri, Sep 7 2007 7:54 AM

...as long as we&#39;re writing code in a static typed language anyway. A couple weeks ago there was

Jeremy D. Miller -- The Shade Tree Developer wrote Working faster and fewer mapping errors with NHibernate
on Wed, Jun 18 2008 10:54 PM

Yesterday, David Laribee related some problems he experienced with refactorings in his domain model leading

Community Blogs wrote Working faster and fewer mapping errors with NHibernate
on Wed, Jun 18 2008 11:01 PM

Yesterday, David Laribee related some problems he experienced with refactorings in his domain model leading

Using StructureMap to mock DataAccess in CSLA BO « maonet technotes wrote Using StructureMap to mock DataAccess in CSLA BO &laquo; maonet technotes
on Wed, Aug 13 2008 11:52 AM

Pingback from  Using StructureMap to mock DataAccess in CSLA BO &laquo; maonet technotes

Jeremy D. Miller -- The Shade Tree Developer wrote A Gentle Quickstart for StructureMap 2.5
on Sun, Nov 30 2008 10:56 PM

The most general question I get with StructureMap is “how do I get started?” Personally, I’d recommend

Community Blogs wrote A Gentle Quickstart for StructureMap 2.5
on Sun, Nov 30 2008 11:48 PM

The most general question I get with StructureMap is “how do I get started?” Personally, I’d recommend

A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas wrote A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas
on Tue, Dec 2 2008 10:16 AM

Pingback from  A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas

A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas wrote A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas
on Wed, Dec 3 2008 11:04 AM

Pingback from  A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas

A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas wrote A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas
on Thu, Dec 4 2008 11:09 AM

Pingback from  A Gentle Quickstart for StructureMap 2.5 - taccato! trend tracker, cool hunting, new business ideas

Cure deployment pain with Environment tests « Jason Neylon's Blog wrote Cure deployment pain with Environment tests &laquo; Jason Neylon&#039;s Blog
on Sun, Jan 31 2010 2:14 PM

Pingback from  Cure deployment pain with Environment tests « Jason Neylon's Blog

Add a Comment

(required)  
(optional)
(required)  
Remember Me?