I’ll be speaking at Tech Ed!

I’ve been meaning to post this for a while, but I’m finally making myself do it now…

I’ll be giving a presentation on “Creating Your Own Enterprise Application Framework” at Tech Ed in Boston, on Friday, June 16 at 9:00 AM in Grand Ballroom A. My talk is based on experiences in my 2–1/2 years in patterns & practices and how I believe we most successfully build our libraries and frameworks. I set out to build a technical talk on this subject but quickly came to the realization that framework and library building is just as much about the softer skills of team building, politics, and negotiation.

If you’re going to be in the area, look me up!

— bab

 

Now playing: Rush – Force Ten

Slides for today’s webcast

Here are the slides from today’s webcast. Thanks who all tuned in.

There will be a slight delay in posting the code from the webcast, as I seem to have accidentally deleted it in the process of getting it ready to be uploaded. I was trying to get rid of all the bin and obj directories, and I did. I also got rid of all the source code  I’ll try to recreate it tonight and post it tomorrow.

Doh!

— bab

Chock full of bloglets

Hi, All,

This entry should have something in it for everybody, since I have a bunch of little things to say.

  • Friday is my last day at patterns & practices. I want to thank everyone there who has put up with me for 2–1/2 years. The experiences I’ve had there and the friends I’ve made there will last me the rest of my life. And hopefully I’ve left something of myself behind that will help them in their future endeavors. And if you’re ever in doubt, guys, just ask yourself, WWBD
  • I will be speaking at TechEd this year, so if you’re going to be there, please look me up. My talk is on Building Your Own Enterprise Framework. I intend to talk about things I learned, both good and bad, during my experiences in building frame^h^h^h^h^hlibraries like EntLib. You can expect this talk to have an agile bent to it
  • I’m headed down to Atlanta next weekend for the FIRST Lego League National Championships. Linsey, my 12 year old daughter, was part of a team of 4 students who won the Missouri state championship. Now they’re off to compete at the Nationals in Atlanta next weekend. And then 2 weeks after that, they’re off to compete in the European championships in Eindhoven as one of 5 US teams invited to compete there.
  • I’m doing a webcast Thursday on Building your own Enterprise Library Design Time. I don’t have the registration link yet, but I’ll post it as soon as I do as a separate blog entry.
  • My friend John Sextro gave a talk at the St. Louis Extreme Programming Users Group last night on the refactoring Move Embellishment to Decorator. In his example, John did the main part of the refactoring in one giant leap. I challenged him to do it in smaller steps, so that he’d have working code throughout the exercise. John took me up on the challenge, and he blogged about how it turned out. Great job, John!
  • This is my 100th blog entry! I’ve been trying to save up for something extra special, but I don’t have time to do a big blog entry right now, and I couldn’t wait any longer to blog these things. So, Happy 100th Posting to Me!

— bab

 

Configuration ExceptionHandling without using an external configuration file

A couple of weeks ago, someone posted a question on the Enterprise Library GDN workspace, asking about how they could configure the Exception Handling block without using an external configuration file. I wrote a bit of code to do that, and I wanted to share it with everyone else.

Here is my solution. This first part is a very small change I made to ExceptionPolicy to allow it to accept an ExceptionPolicyFactory through an additional HandledException method.

//===============================================================================
// Microsoft patterns & practices Enterprise Library
// Exception Handling Application Block
//===============================================================================
// Copyright © Microsoft Corporation. All rights reserved.
// Adapted from ACA.NET with permission from Avanade Inc.
// ACA.NET copyright © Avanade Inc. All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System;
using System.Collections;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Properties;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Instrumentation;

namespace Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
{
    /// 
    /// Represents a policy with exception types and
    /// exception handlers. 
    /// 
    public static class ExceptionPolicy
    {
        private static readonly ExceptionPolicyFactory policyFactory = new ExceptionPolicyFactory();

        public static bool HandleException(Exception exceptionToHandle, string policyName)
        {
            return HandleException(exceptionToHandle, policyName, policyFactory);
        }

        public static bool HandleException(Exception exceptionToHandle, string policyName, ExceptionPolicyFactory factory)
        {
            if (exceptionToHandle == null) throw new ArgumentNullException("exceptionToHandle");
            if (factory == null) throw new ArgumentException("factory");
            if (string.IsNullOrEmpty(policyName)) throw new ArgumentException(Resources.ExceptionStringNullOrEmpty);

            ExceptionPolicyImpl policy = GetExceptionPolicy(exceptionToHandle, policyName, factory);

            return policy.HandleException(exceptionToHandle);
        }

        private static ExceptionPolicyImpl GetExceptionPolicy(Exception exception, string policyName, ExceptionPolicyFactory factory)
        {
            try
            {
                return factory.Create(policyName);
            }
            catch (ConfigurationErrorsException configurationException)
            {
                try
                {
                    DefaultExceptionHandlingEventLogger logger = EnterpriseLibraryFactory.BuildUp();
                    logger.LogConfigurationError(configurationException, policyName);
                }
                catch { }
                throw;
            }

            catch (Exception ex)
            {
                try
                {
                    string exceptionMessage = ExceptionUtility.FormatExceptionHandlingExceptionMessage(policyName, ex, null, exception);

                    DefaultExceptionHandlingEventLogger logger = EnterpriseLibraryFactory.BuildUp();
                    logger.LogInternalError(policyName, exceptionMessage);
                }
                catch { }

                throw new ExceptionHandlingException(ex.Message, ex);
            }
        }
    }
}

And here is an example I took from the ExceptionHandling basic quickstart. If you take this code and put it in the QuickStartForm.Main method and adjust the constructor for AppService to take an ExceptionPolicyFactory, then you should have a complete example. I’ve tried to indent the construction of the objects to make it more clear about what is being created and how it all gets hooked up.

[STAThread]
static void Main()
{
    DictionaryConfigurationSource internalConfigurationSource = new DictionaryConfigurationSource();
        ExceptionHandlingSettings settings = new ExceptionHandlingSettings();
            NamedElementCollection policyData = settings.ExceptionPolicies;
                ExceptionPolicyData globalPolicyData = new ExceptionPolicyData("Global Policy");
                    NamedElementCollection globalPolicyExceptionTypes = globalPolicyData.ExceptionTypes;
                        ExceptionTypeData exceptionType = new ExceptionTypeData("Exception", typeof(Exception), PostHandlingAction.None);
                            NameTypeConfigurationElementCollection exceptionTypeHandlers = exceptionType.ExceptionHandlers;
                            exceptionTypeHandlers.Add(new CustomHandlerData("Custom Handler", typeof(AppMessageExceptionHandler)));
                    globalPolicyExceptionTypes.Add(exceptionType);
                ExceptionPolicyData handleAndResumeData = new ExceptionPolicyData("Handle and Resume Policy");
                    NamedElementCollection handleAndResumeExceptionTypes = handleAndResumeData.ExceptionTypes;
                    handleAndResumeExceptionTypes.Add(exceptionType);
                ExceptionPolicyData propagatePolicyData = new ExceptionPolicyData("Propagate Policy");
                    NamedElementCollection propagatePolicyExceptionTypes = propagatePolicyData.ExceptionTypes;
                        ExceptionTypeData exceptionWithRethrowType = new ExceptionTypeData("Exception", typeof(Exception), PostHandlingAction.NotifyRethrow);
                    propagatePolicyExceptionTypes.Add(exceptionWithRethrowType);
                ExceptionPolicyData replacePolicyData = new ExceptionPolicyData("Replace Policy");
                    NamedElementCollection replacePolicyExceptionTypes = replacePolicyData.ExceptionTypes;
                        ExceptionTypeData securityExceptionType = new ExceptionTypeData("SecurityException", typeof(SecurityException), PostHandlingAction.ThrowNewException);
                            NameTypeConfigurationElementCollection securityExceptionTypeHandlers = securityExceptionType.ExceptionHandlers;
                            securityExceptionTypeHandlers.Add(new ReplaceHandlerData("Replace Handler", "Replaced Exception: User is not authorized to peform the requested action.", typeof(ApplicationException)));
                    replacePolicyExceptionTypes.Add(securityExceptionType);
                ExceptionPolicyData wrapPolicyData = new ExceptionPolicyData("Wrap Policy");
                    NamedElementCollection wrapPolicyExceptionTypes = wrapPolicyData.ExceptionTypes;
                        ExceptionTypeData dbConcurrencyExceptionType = new ExceptionTypeData("DBConcurrencyException", typeof(DBConcurrencyException), PostHandlingAction.ThrowNewException);
                            NameTypeConfigurationElementCollection dbConcurrencyExceptionTypeHandlers = dbConcurrencyExceptionType.ExceptionHandlers;
                            dbConcurrencyExceptionTypeHandlers.Add(new WrapHandlerData("Wrap Handler", "Wrapped Exception: A recoverable error occurred while attempting to access the database.", typeof(BusinessLayerException)));
                    wrapPolicyExceptionTypes.Add(dbConcurrencyExceptionType);
                  
                policyData.Add(globalPolicyData);
                policyData.Add(handleAndResumeData);
                policyData.Add(propagatePolicyData);
                policyData.Add(replacePolicyData);
                policyData.Add(wrapPolicyData);

        internalConfigurationSource.Add(ExceptionHandlingSettings.SectionName, settings);
        policyFactory = new ExceptionPolicyFactory(internalConfigurationSource);

    AppForm = new QuickStartForm();
    Application.Run(AppForm);
}

If this example isn’t clear to you, please let me know, and I can try to answer any questions that any of you may still have.
— bab
 

Webcast Announcement – Thursday, February 9, 1100-1200 PST

This Thursday, Tom Hollander and I will be presenting a web cast on building your own application block using Enterprise Library January 2006. The talk will discuss how to take a piece of functionality that you have built and plug it into the configuration system, so that it can be constructed using our provider-based factories.

The topics will include:

  1. Building the simplest Application Block possible
  2. Attaching configuration to the block
  3. Allowing for variability through custom providers
  4. Creating your own configuration objects for your providers

And if time allows, I’ll be discussing how to create and attach instrumentation to your new application block.

Tell all your friends — it should be a blast. Look for the signup link to be posted early this week.

— bab

 

Enterprise Library and Object Builder

One of the biggest areas of change between this new version of Enterprise Library and the original version, shipped a year ago, is our configuration system. The original configuration system, written (and rewritten 3x) by Scott Densmore, worked tremendously well, but was custom-written just for Enterprise Library. In our newer version, we’ve adopted a more reusable framework on which to base our configuration system. This framework, called Object Builder, is a reusable, configurable dependency injection and object creation pipeline written as part of the CAB project by Peter Provost and Brad Wilson, with lots of goals donated by Ed Jezierski. As part of our code reuse initiative, we decided it would be a good idea if both CAB and Enterprise Library could ship with a similar underlying infrastructure, just so we have a better common story to tell between the two projects.

Brief discussion of what Object Builder is

Object Builder (OB) is, as I mentioned previously, is basically a pipeline that allows you to customize how an object is created. You talk to it by saying something like:

    MyFoo foo = ObjectBuilder.BuildUp<MyFoo>(“FooInstanceName”);

Looks really simple, right? Well, what actually happens during that call can be changed, and tailored, and twisted to be almost anything you want. The creational process through OB can be customized by registering Strategies with your OB instance. These Strategies customize the trip through the pipeline by adding steps to your object’s journey. Strategies are added into different phases of creation, including PreCreation, Creation, Initialization, and PostInitialization, based on how you add them to OB, depending on when your particular operation needs to happen. For Enterprise Library’s purposes, for instance, we have several steps that take place during PreCreation, a Creation strategy, and a PostCreation strategy that all happen while building objects, and we add the strategies responsible for implementing them to our own OB instance in a class called EnterpriseLibraryFactory.

Each Strategy that is registered with OB can gather information and context at runtime, when it is invoked, by querying any one of several Policy instances that can be associated with a particular Strategy. You can use this to use the same Strategy for several purposes, and inform the Strategy about the details of its purpose using a Policy at runtime.

Enterprise Library and its factories

There are two basic kinds of blocks in Enterprise Library, characterized by how you create them. There are those blocks where the caller explicitly instantiates the block and makes calls to it. For example, when you want to make a database call, you talk to the DatabaseFactory and ask for a specific Database instance, as

    Database db = DatabaseFactory.CreateDatabase(“Sales”);

Caching, Data Access, Security, and Cryptography each work this way, and we’ll call these instance-based blocks for our purposes in this article. Each of these blocks provides a static factory class that can be used to create instances of the block, as shown above. These static factories don’t do much work themselves, but only serve as a convenience for our callers. The actual work of orchestrating the creation of our objects happens in the instance factories called from their static brethren.

There are also other blocks that are accessed exclusively through static instances. Logging and Exception Handling both work this way. When you want to log a message, you don’t have to create an instance of the Logging block and then call Write. Instead, you talk to a single, global instance of the Logger, as

    Logger.Write(“My message”);

We’ll call these facade-based blocks in this article, for our convenience. Now, what you may not know is that inside facade-based blocks, there are still objects that are created for each call to the block. These per-call objects are created through instance-based factories as described above, which allows me to explain our creation process one time and have it apply to all blocks (lucky for me!).

And now that I’ve made the case for how each of the blocks shares many similar aspects as far as how they create their objects through these instance-based factories, let me just add that these factories don’t really do very much. Their only real responsibilities are to act as an adapter layer between the static facades of each of the blocks and the underlying generic interfaces of Object Builder. These instance factories basically just allow us to put a type onto the object that gets created through OB, and that’s about where their job ends. The really interesting stuff happens in how Enterprise Library uses Object Builder, and that’s what the rest of this article is about.

How Enterprise Library uses Object Builder

Enterprise Library has a few interesting requirements for how we build our objects that were interesting to implement. I didn’t do most of the heavy lifting on this — it was done in large part by Fernando Simonazzi of Clarius. Fernando went through several iterations of his implementation until we could find something that both made sense and was performant enough for us to accept.

The two biggest issues we had in adopting OB for our own were that we needed didn’t always know the exact type of the object we were trying to create (SqlDatabase versus OracleDatabase, for example), and we needed to drive all of our creation through configuration. Neither of these requirements were directly implemented in OB, so we had to write our own strategies to get them to happen. These strategies allowed us create our objects using code that looked like

    Database db = DatabaseFactory.CreateDatabase();

In that seemingly simple line of code, lots of things had to happen to give you back to exact type and specific instance of a database that was needed.

In a nutshell, these are the steps we needed. Don’t worry if these steps aren’t clear now — I’ll be explaining them in detail immediately afterwards. 

  1. Determine the name of the instance to construct. Users can either provide the instance name to us, or they can tell us to create the default instance, which requires some work on our part to find the correct instance name to use.
  2. Figure out if the instance being requested is a singleton or not. If it is a singleton and it has already been created, then just return the already created object, otherwise allow the strategy chain to proceed.
  3. Take the instance name we’ve discovered and create an instance of that object, driven through configuration.
  4. Attach any needed instrumentation objects to the freshly created object instance.
  5. Hand the object back to the original caller.

And the biggest design goal we had to keep in mind throughout this process is that we had to make it easy for someone else to come in later, read, and understand how the whole instantiation process happens, and be able to hook their own blocks and providers into it as well. Sounds pretty simple, eh?? What follows is our best shot at fulfilling all these goals. Read on, ask questions, and we’ll end up with a pretty good explanation of how it all works by the end.

Build Steps

Determining the name of default instances

As I said before, many of our blocks allow users to define a default instance of a block. This is what lets you ask for an instance of the Caching block, for example, without needing to specify that named instance to create. But for us to instantiate your CacheManager object, we have to figure out what name you assigned as the default. Conveniently for us, this is defined in your configuration file, as

 <cachingConfiguration defaultCacheManager=”ReferenceData”>
 </cachingConfiguration>

This is where our first custom strategy comes in. ConfigurationNameMappingStrategy is responsible for translating from the empty instance name and a configuration file to the appropriate default instance name. Now it can’t do this on its own, since it doesn’t know how each block’s configuration section looks, so it needs some help. It gets this help by looking at the class being created for a specific attribute, the ConfigurationNameMapperAttribute, that describes a helper class that can parse the configuration and can figure out the name of the default instance. Here, the ConfigurationNameMapperAttribute is shown on the CacheManager, pointing to the type CacheManagerDataRetriever.

 [ConfigurationNameMapper(typeof(CacheManagerDataRetriever))]
    public class CacheManager : IDisposable
    {
    }

DataRetrievers are a category of objects we’ve created as helpers whenever we need to read some sort of configuration information. In this case. the CacheManagerDataRetriever implements IConfigurationNameMapper, whose responsibility it is to know how to read a configuration section, parse it, and return the default instance name.

The ConfigurationNameMappingStrategy knows the type of the object it is being asked to create, uses this type to reflect and find the ConfigurationNameMapperAttribute, instantiates an object of this type, reads the default instance name, and uses that to drive the rest of the creation process. And that’s all there is to it  

Looking for singletons

Most of our classes in Enterprise Library are instantiated as they are needed. But for some objects, for various reasons, we don’t create a new instance each time. The Caching block, for example, needs to reuse the same CacheManager instance each time, so that it can keep the in-memory and on-disk representations of the cache consistent between different cache invocations. And other blocks, such as Exception Handling, reuse the same set of objects each time to save the instantiation cost, since it is very expensive to create all the configuration objects this block needs.

The way that this is managed in Enterprise Library is by asking OB to remember instances of certain blocks for us. When we ask OB to create instances of those blocks for us, it first checks to see if it already has an instance lying around with the appropriate name for us. If so, it hands that instance back to us, otherwise, it creates a new instance, caches it, and returns it.

The way OB can tell whether or not to treat an instance of a singleton or not is through ObjectBuilder concepts called Locators and LifetimeContainers. LifetimeContainers, in OB-speak, manage the lifetime of named instances of objects. You can register an object and its name with a LifetimeContainer, and it will keep that object alive until such time as you dispose of the LifetimeContainer. And every LifetimeContainer is associated with a Locator. A Locator acts as a dictionary that maps instance names to objects, but it does it using WeakReferences — this means that storing something in a Locator will not prevent that object from being GCed. Tot keep an object alive, you need to provide the LifetimeContainer as well, which is what keeps the references in use, preventing the object from being scavenging out of existence.

There is very little custom code that we had to write in Enterprise Library to implement this singleton ability, as Object Builder had it most of it built in. All we needed to do was to have the instance-based factories that wanted this functionality inherit from LocatorNameTypeFactoryBase<T>, a type we wrote for Enterprise Library. This base class created a ReadWriteLocator, for object lookups, and a LifetimeContainer, to provide for the persistence of the objects, and ensured that these infrastructure objects were used whenever we used OB to instantiate our objects. All of this magic is hidden from our callers.

Here is the CacheManagerFactory, as an example:

    public class CacheManagerFactory : LocatorNameTypeFactoryBase<CacheManager>
    {
        /// 
        /// Initializes a new instance of the  class 
        /// with the default configuration source.
        /// 
        protected CacheManagerFactory()
            : base()
        {
        }
 
        /// 
        /// Initializes a new instance of the  class 
        /// with the given configuration source.
        /// 
             /// The configuration source that contains information on how to build the  instances
        public CacheManagerFactory(IConfigurationSource configurationSource)
            : base(configurationSource)
        { }
    }

As you can see, the base class contains all the singleton-related magic, which we can access just by inheriting from the base class I described.

Instantiating the object

OK, now that the preliminaries are out of the way, we’re ready for the really interesting, and slightly complicated, part of this — actually instantiating the object. This is where we take the configuration data, metadata available through attributes, and other custom code, shake them up, and spit out the object we want. Hold on tight, because there are a few different steps that have to happen here, and each of them has to happen just right for our object to be created.

 It all starts with the ConfiguredObjectStrategy, which is one of the strategies we plug into OB as a PreCreation strategy. All this strategy does is to look for the object type being instantiated, retrieve a custom-built factory that knows how to instantiate an object of that type, and make a call to that factory. It figures out the kind of custom factory to use by using the CustomFactoryAttribute placed on the class being instantiated:

 [CustomFactory(typeof(CacheManagerCustomFactory))]
    public class CacheManager : IDisposable
    {
    }

Note: Sharp readers will have noticed that the CacheManager is attributed with two different attributes, each used during different phases of object creation. The ConfigurationNameMapperAttribute is used to figure out the default instance name for a block, and the CustomFactoryAttribute describes how to instantiate block instances.

The ConfiguredObjectStrategy instantiates this factory, and delegates the responsibility of orchestrating the creation of the requested object to it.

Inside of all of these factories, the same set of core things happens.

  1. Each custom factory knows which configuration class contains the configuration information. This is generally hardcoded in each custom factory, since that factory knows which configuration classes that it needs to create the objects it is responsible for. It also knows how to read the given configuration information to create an instance of its configuration class.
  2. Inspects the configuration class to find the Assembler that is used actually construct the final object. Assemblers have the responsibility of instantiating the final object being created. They are hardcoded classes that know the types of objects they’re going to create, know how to call the constructor of that class, and do any extra work needed to initialize that object before returning it.

Again, most of this logic is contained in base classes we’ve provided. Here we see the SymmetricCryptoProviderCustomFactory which is responsible for creating your symmetric cryptographic provider. It has almost no code in it, as it inherits its functionality from its bases:

   public class SymmetricCryptoProviderCustomFactory : AssemblerBasedCustomFactory<ISymmetricCryptoProvider, SymmetricProviderData>
    {
        protected override SymmetricProviderData GetConfiguration(string name, IConfigurationSource configurationSource)
        {
            return new CryptographyConfigurationView(configurationSource).GetSymetricCryptoProviderData(name);
        }
    }

 And in its base class, AssemblyerBasedCustomFactory, we add the Create call:

  public abstract class AssemblerBasedCustomFactory<TObject, TConfiguration> : AssemblerBasedObjectFactory<TObject, TConfiguration>, ICustomFactory
        where TObject : class
        where TConfiguration : class
    {
        public TObject Create(IBuilderContext context, string name, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
        {
            TConfiguration objectConfiguration = GetConfiguration(name, configurationSource);
            TObject createdObject = Create(context, objectConfiguration, configurationSource, reflectionCache);

            return createdObject;
        }

        protected abstract TConfiguration GetConfiguration(string name, IConfigurationSource configurationSource);
    }

And its base class adds the algorithm to actually cause the instantiations to happen:

   public abstract class AssemblerBasedObjectFactory<TObject, TConfiguration>
        where TObject : class
        where TConfiguration : class
    {
        public virtual TObject Create(IBuilderContext context, TConfiguration objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
        {
            IAssembler<TObject, TConfiguration> assembler = GetAssembler(objectConfiguration);
            TObject createdObject = assembler.Assemble(context, objectConfiguration, configurationSource, reflectionCache);

            return createdObject;
        }

        private IAssembler<TObject, TConfiguration> GetAssembler(TConfiguration objectConfiguration)
        {
            Type type = objectConfiguration.GetType();
            AssemblerAttribute assemblerAttribute = GetAssemblerAttribute(type);

            return (IAssembler<TObject, TConfiguration>)Activator.CreateInstance(assemblerAttribute.AssemblerType);
        }

        private AssemblerAttribute GetAssemblerAttribute(Type type)
        {
            AssemblerAttribute assemblerAttribute 
                = Attribute.GetCustomAttribute(type, typeof(AssemblerAttribute)) as AssemblerAttribute;
            return assemblerAttribute;
        }
    }

 And finally, the last link in the chain, an Assembler implementation:

 public class SymmetricAlgorithmProviderAssembler : IAssembler<ISymmetricCryptoProvider, SymmetricProviderData>
    {
        public ISymmetricCryptoProvider Assemble(IBuilderContext context, SymmetricProviderData objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
        {
            SymmetricAlgorithmProviderData castedObjectConfiguration
                = (SymmetricAlgorithmProviderData)objectConfiguration;

            ISymmetricCryptoProvider createdObject
                = new SymmetricAlgorithmProvider(
                        castedObjectConfiguration.AlgorithmType,
                        castedObjectConfiguration.ProtectedKeyFilename,
                        castedObjectConfiguration.ProtectedKeyProtectionScope);

            return createdObject;
        }
    }

Attaching Instrumentation

The final step in this process is to attach any needed instrumentation to the objects just created. This is a completly separate process from object instantiation and deserves an entire post of its own, which is what I’m going to do. My next post will be about how instrumentation in Enterprise Library works, how it is instantiated, and attached to the objects it is observing. It is completely different from the original implementation, with the intent to allow it to be turned on and off through configuration, and to allow instrumentation handling code to be kept separate from instrumentation reporting code. More on these topics later.

Things I’ve (intentionally) left out

In the process of explaining this stuff, I’ve simplified some concepts along the way. I intend to go back and offer other blog entires on these topics. They are subjects that build on this basic understanding we’ve gained from this post, so I wanted to let people read and digest this first before adding new information on top of it.

The first topic I omitted from this post is actually a performance optimization we had to add late in our development cycle. As you can tell, this factory code we created is heavily attribute based, and reading attributes at runtime is really, really slow. We had our entire system created and working, profiled it, and found out that we were several times slower than our 1.0 and 1.1 releases, which is obviously not acceptable. Fernando implemented a system where we cache the objects we instantiate via attribute reflection, so we only have to pay this price one time. This brought our performance up to where we needed it almost completely. But this attribute caching is purely an implementation detail of how the internals work and doesn’t affect the overall design much at all (as it should be!).

The other topic I omitted for now is the whole idea of polymorphic configuration and object creation. In lots of places, we know the base type of the object we want to create, but we don’t know the actual concrete type. For example, we may know that we need an ISymmetricCryptoProvider, but we don’t know if we need a SymmetricCryptoProvider or a DPAPISymmetricCryptoProvider. We can find this out through configuration, but I haven’t discussed how that works yet. That, again, is a topic for another post.

Summary

This, in its high-level entirety, is the object creation process inside Enterprise Library. We went down this road because we wanted to use Object Builder as the driver for our factories. It is one part experiment and one part an attempt to reuse code and policies inside p&p. I think it worked out rather well, as it gave us a reusable pipeline into which we can inject strategies that govern how our creation process works. We can modify when and where objects are created and bound together without having to go change explicit code for the most part, which is an interesting win for us.

The basics of the whole process start with the block instance factory. These classes call into EnterpriseLibraryFactory, which starts us on our trip through ObjectBuilder. This trip allows us to translate an empty name into a default name, implement a singleton pattern completely external to the object being made into a singleton, and create an object entirely based on configuration. Inside each object being instantiated, there is an attribute that describes the custom mfactory that knows how to build that object. This factory uses the configuration class for the object being built to find the assembler that can create the actual object, instantiates the configuration object and assembler, and tells the assembler to create the final object, and voila, we’re finished!

Request for comments

This is my first attempt at explaining this topic. Its a bit complicated and there is a chance that my explanation may add to some level of confusion. If I’ve explained something badly, or you have a question about what I really meant, please ask me. I intend to edit and update this blog posting as I learn more about my feedback, until it actually does explain what I want it to. So, keeps those cards and letters coming!

— bab

 

Now playing: AC/DC – Dirty Deeds Done Dirt Cheap – Big Balls

Now playing: AC/DC – Dirty Deeds Done Dirt Cheap – Big Balls

patterns & practices Agile and Extreme Programming Lessons Learned webcast link

Sorry I didn’t get this link out in time for folks to tune in live, but I spaced on it  Anyhow, Peter Provost, Brad Wilson, Darrell Snow, and I did a webcast about a week ago talking about lessons learned during our projects (EnterpriseLibrary and CAB). We ran both of these projects using similar Agile methods, had great results in both, and talked about what went well and what we wanted to improve on for next time.

If you feel the urge to listen in to our conversations from this webcast, here is the link.

— bab