Jul 18 2010

Speaking at CodeCampOz

Category: IT Related | .Net | ApplicationsRory Primrose @ 04:19

Mitch has just posted the agenda for CodeCampOz that is running in November. Looks like it will be a really good mix of information being presented this year.

I’ll be running a session on Windows Identity Framework and how to use it without federation. Here is the abstract for my session.

Not a WIF of federation

The Windows Identify Framework (WIF) provides the latest Microsoft implementation for working in the claims-based identity space. WIF has particular strengths in providing federated security for systems that target users across multiple security domains, multiple credential types and multiple credential stores.

Unfortunately the available WIF documentation and samples almost completely deal with federated security scenarios. The information provided continues to use federated security architectures (Security Token Services, Issuing Authorities, Relying Parties etc.) even when federation is not used.

Developers of small systems may find it difficult to understand how WIF fits into their system designs. Small systems in this context tend to have their own security store, do not cross security domains and may not even run within an Active Directory managed domain.

There are clear benefits with using claims based security in both large and small systems. How do developers leverage claims-based security without being tied to federated security architectures?

This session will briefly cover the benefits of claims-based security and then look at how to implement WIF in ASP.Net and WCF applications without federation dependencies.

Tags: ,

Jul 17 2010

Creating proxies with RealProxy

Category: .Net | .NetRory Primrose @ 14:53

I first came across the RealProxy class three years ago when trying to figure out better ways of handling WCF client proxies (correct connection disposal, reusing faulted channels etc). I was intrigued about how ChannelFactory<T> could return an instance of a service contract that had the implementation of a WCF client for an interface definition it knows nothing about.

With a decent amount of Reflector surfing I followed the rabbit hole down to the usage of RealProxy. In my opinion this has to be one of the most interesting classes in the CLR. I finally founds some time to post some information about this amazing class.

Imagine a scenario where you want to have an action that you want to take when a method on a type is invoked when you do not have any prior knowledge of the type definition. The RealProxy class is able to run some magic that will return you a proxy instance of that type and provide notification when method invocations occur on that proxy. The main downside with RealProxy is that the type to proxy must either be an interface or a class that inherits from MarshalByRefObject.

The following is an example of a RealProxy implementation that outputs method invocations to the console.

public class TestProxy<T> : RealProxy
{
    public TestProxy()
        :base(typeof(T))
    {
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
    [DebuggerStepThrough]
    public override IMessage Invoke(IMessage msg)
    {
        Debug.Assert(msg != null, "No message has been provided");

        ReturnMessage responseMessage;
        Object response = null;
        Exception caughtException = null;

        try
        {
            String methodName = (String)msg.Properties["__MethodName"];
            Type[] parameterTypes = (Type[])msg.Properties["__MethodSignature"];
            MethodBase method = typeof(T).GetMethod(methodName, parameterTypes);

            Object[] parameters = (Object[])msg.Properties["__Args"];

            // ______________________________________________________________________________
            //
            // Start the logic for handling the method invocation on this proxy
            // ______________________________________________________________________________

            Console.WriteLine("Invoking " + method.Name + " with the following parameters:");

            for (int index = 0; index < parameters.Length; index++)
            {
                Console.WriteLine(parameterTypes[index].Name + " - " + parameters[index]);
            }

            // ______________________________________________________________________________
            //
            // End the logic for handling the method invocation on this proxy
            // ______________________________________________________________________________
        }
        catch (Exception ex)
        {
            // Store the caught exception
            caughtException = ex;
        }

        IMethodCallMessage message = msg as IMethodCallMessage;

        // Check if there is an exception
        if (caughtException == null)
        {
            // Return the response from the service
            responseMessage = new ReturnMessage(response, null, 0, null, message);
        }
        else
        {
            // Return the exception thrown by the service
            responseMessage = new ReturnMessage(caughtException, message);
        }

        // Return the response message
        return responseMessage;
    }
}

The class informs RealProxy about the type to proxy when it calls the base constructor. Invocations of the proxy methods cause the Invoke method to be called. The Invoke parameters provide information about the method name, parameter types and parameter values for the method invocation on the proxy instance. The Invoke method handles any exceptions so that they can be processed correctly by the proxy and be correctly thrown in the calling code. A return value (or null for void methods) are processed in the return message if no exception was found.

The key for working with a RealProxy instance is how the proxy is created. The proxy instance is not the class that inherits from RealProxy. That class is simply runs the processing of methods called on the proxy instance. The proxy instance is actually created from the RealProxy class using the GetTransparentProxy() method.

The following is a console application that uses an interface definition to test the above proxy implementation.

class Program
{
    static void Main(string[] args)
    {
        TestProxy<ITester> proxy = new TestProxy<ITester>();
        ITester tester = proxy.GetTransparentProxy() as ITester;

        tester.RunTest("This is the message", true, Guid.NewGuid());

        Console.ReadKey();
    }
}

public interface ITester
{
    void RunTest(String message, Boolean flag, Guid marker);
}

This console application uses the TestProxy<T> class to create a proxy for the ITester interface. Invocations of the proxy instance then cause TestProxy<T>.Invoke to process the invocation. TestProxy in this case will output information about the method invocation to the console. The output for this example is something like the following.

Invoking RunTest with the following parameters:
String - This is the message
Boolean - True
Guid - aade8728-9ca3-4919-9080-14090b5b016b

That’s all there is to it for rolling your own proxy implementation.

Tags: ,

Jul 12 2010

UnityServiceBehavior updated to support named resolutions

Category: .NetRory Primrose @ 17:43

I previously posted about how to get Unity support in WCF services via a ServiceBehavior. I found a glaring omission in this implementation when preparing for my .Net Users Group presentation. The existing code supports named configuration sections and named containers. Unfortunately it left out the more common named instance resolutions.

I have checked in a new code version that supports this. See UnityServiceElement, UnityServiceBehavior and UnityInstanceProvider for the updated code line.

Tags: ,

Jul 12 2010

Canberra .Net Users Group Presentation next week

Category: .Net | ApplicationsRory Primrose @ 17:32

I’m going to be presenting at this months .Net Users Group in Canberra. The topic will be Unity injection for ASP.Net and WCF with some Unity extensibility added in as well.

Here is the abstract for the session.

Unity is the Microsoft Patterns and Practises implementation of an inversion of control (IoC) container. Using IoC containers facilitates the dependency injection pattern which helps to decouple code from its dependencies.

A common way to implement IoC in ASP.Net and WCF services is to couple the hosted application to the IoC container. The first half of this session will look at how ASP.Net and WCF applications can be extended to leverage the benefits of IoC while decoupling the hosted application from the container.

The second half of the session will look at how Unity can be extended to provide a recursive disposal pattern for build trees created by the container.

If you are local then I hope to see you there.

Tags: , , , ,

Jul 7 2010

Unity build failure recovery for DisposableStrategyExtension

Category: .Net | ApplicationsRory Primrose @ 17:41

I posted recently about my Unity extension that disposes build trees when a container tears down an instance it previously created. The extension makes an assumption that a Unity build operation will either succeed completely or fail completely. Normally you expect this to be the case. I have however now come up with an edge case.

I am writing another Unity extension that adds support for injecting proxy instances as dependencies. Part of this design allows for custom proxy handlers to be injected. Defining a custom proxy handler is optional and can either be specifically defined in configuration at the injection definition or be automatically resolved by the Unity container from contextual information. If a custom proxy isn’t defined or can’t be automatically resolved then the injection will fall back to a default proxy handler type.

This new extension creates a child build context within the build context of the proxy dependency being created by the Unity container. The child build context will attempt to create the proxy handler just in case the container is configured for it. Unity will throw an exception if the proxy handler isn’t defined. In this case however, the build operation needs to continue rather than failing like you would normally expect.

This becomes a problem for the DisposableStrategyExtension. It tracks build trees as they are created using the BuildTreeTracker class mentioned in the previous post. It tracks when a new build context is started and when it is finished. Each iteration of this process tracks the current tree node being built by the context. Each child build context encountered results in a new child node that is simply added to the current node. The child node then becomes tracked as the current node being built. Unfortunately there is no fault tolerance for when an item fails to be built when the overall build operation can continue.

Consider the following build tree for example.

  • Root
    • ChildA
      • DependencyA
      • DependencyB
    • ChildB

The PreBuildUp method in BuildTreeTracker gets invoked before each tree node is created and PostBuildUp gets invoked after each node (and it’s children) are completely built. The BuildTreeTracker class tracks the start and end of each item being built via these two methods. What happens if the creation of ChildA fails without any recovery action? Assuming the build process handles the build failure exception and continues the build, the BuildTreeTracker identifies the current node as ChildA when it goes to create ChildB (PreBuildUp) however it should actually point to Root. This is because the tracker re-points the current node in the build tree back to the parent of the current node on PostBuildUp. Unfortunately PostBuildUp does not get invoked when a build context fails.

Enter the IRequiresRecovery interface. This interface allows for some recovery operation to be invoked when a build context has failed to create an instance.

using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using Microsoft.Practices.ObjectBuilder2;

namespace Neovolve.Toolkit.Unity
{
    internal class BuildTreeRecovery : IRequiresRecovery
    {
        public BuildTreeRecovery(IBuilderContext context, BuildTreeItemNode failedNode, Action<BuildTreeItemNode> failureAction)
        {
            Contract.Requires<ArgumentNullException>(context != null, "The context parameter is null");
            Contract.Requires<ArgumentNullException>(failedNode != null, "The failedNode parameter is null");

            Context = context;
            FailedNode = failedNode;
            FailureAction = failureAction;
        }

        public void Recover()
        {
            try
            {
                if (FailureAction != null)
                {
                    FailureAction(FailedNode);
                }

                BuildTreeItemNode parentNode = FailedNode.Parent;

                BuildTreeDisposer.DisposeTree(Context, FailedNode);

                if (parentNode != null)
                {
                    parentNode.Children.Remove(FailedNode);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Recovery failed: " + ex);
            }
        }

        protected IBuilderContext Context
        {
            get;
            private set;
        }

        protected BuildTreeItemNode FailedNode
        {
            get;
            private set;
        }

        protected Action<BuildTreeItemNode> FailureAction
        {
            get;
            private set;
        }
    }
}

The BuildTreeRecovery class allows for a custom action to be invoked. It then cleans up any partial build trees that were successfully created under the failed node and then removes it from its parent. If you assume in the previous example that DependencyA and DependencyB were successfully created before ChildA failed, then BuildTreeRecovery will ensure that ChildA, DependencyA and DependencyB are all disposed and that the failed node (ChildA) is then removed from the build tree.

The final piece missing here is that the BuildTreeTracker class will still have a reference to ChildA being the current node instead of Root when PreBuildUp is invoked for ChildB. This is where the custom action of the recovery class comes in. The BuildTreeTracker.PreBuildUp method has been updated to create the BuildTreeRecovery class and use it in a recovery stack. The action passed to its constructor is a lambda expression that repairs the assignment of the current node in the build tree to be the parent of the failed node.

public override void PreBuildUp(IBuilderContext context)
{
    base.PreBuildUp(context);

    if (context == null)
    {
        return;
    }

    Boolean nodeCreatedByContainer = context.Existing == null;
    BuildTreeItemNode newTreeNode = new BuildTreeItemNode(context.BuildKey, nodeCreatedByContainer, CurrentBuildNode);

    if (CurrentBuildNode != null)
    {
        // This is a child node
        CurrentBuildNode.Children.Add(newTreeNode);
    }

    CurrentBuildNode = newTreeNode;

    BuildTreeRecovery recovery = new BuildTreeRecovery(context, newTreeNode, failedNode => CurrentBuildNode = failedNode.Parent);

    context.RecoveryStack.Add(recovery);
}

Completing the example proposed above, the resultant build tree when ChildA fails to build would be:

  • Root
    • ChildB

While using this recovery class satisfies my optional build requirement for proxy injection, it provides an arguably more important feature. Build trees might fail at anytime and applications may recover from or simply ignore the failures and continue. The partial build trees created in these attempts may have created some references that require disposal but this can never occur because the original BuildTreeTracker code would simply lose track of nodes successfully created in a failed build tree. This solution will ensure that these instances are disposed immediately if the build operation fails.

Tags:

Jul 5 2010

ConnectionStringSettings parameter injection in Unity

Category: Rory Primrose @ 18:15

I have previously posted about supporting AppSetting value resolution for Unity injection (Unity 1.x here and Unity 2.0 here). Today I had a requirement to inject connection string values from application configuration. Like the AppSetting implementation, this creates a nice redirection of injection values as developers and administrators are more familiar with the connection string section in application configuration compared to finding the right value in large amounts of Unity configuration.

This implementation leverages the AppSettingParameterValueExtension class from the prior examples and renames it to SectionExtensionInitiator. This class now configures a Unity section for multiple element extensions rather than a single specific implementation.

using Microsoft.Practices.Unity.Configuration;

namespace Neovolve.Toolkit.Unity
{
    public class SectionExtensionInitiator : SectionExtension
    {
        public override void AddExtensions(SectionExtensionContext context)
        {
            if (context == null)
            {
                return;
            }
 
            context.AddElement<AppSettingsParameterValueElement>(AppSettingsParameterValueElement.ElementName);
            context.AddElement<ConnectionStringParameterValueElement>(ConnectionStringParameterValueElement.ElementName);
        }
    }
}

The ConnectionStringParameterValueElement is responsible for creating an injection value for a parameter. It can be used to create a parameter for either the String or ConnectionStringSettings types.

using System;
using System.Configuration;
using System.Globalization;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Neovolve.Toolkit.Storage;
using Neovolve.Toolkit.Unity.Properties;

namespace Neovolve.Toolkit.Unity
{
    public class ConnectionStringParameterValueElement : ParameterValueElement
    {
        public const String ElementName = "connectionSetting";

        public ConnectionStringParameterValueElement()
        {
            Config = ConfigurationStoreFactory.Create();
        }

        public ConnectionStringSettings CreateValue()
        {
            ConnectionStringSettings configurationValue = Config.GetConnectionSetting(ConnectionStringKey);

            if (configurationValue == null)
            {
                String message = String.Format(CultureInfo.InvariantCulture, Resources.ConnectionStringParameterValueElement_ConnectionStringKeyNotFound, ConnectionStringKey);

                throw new ConfigurationErrorsException(message);
            }

            return configurationValue;
        }

        public override InjectionParameterValue GetInjectionParameterValue(IUnityContainer container, Type parameterType)
        {
            if (parameterType == null)
            {
                throw new ArgumentNullException("parameterType");
            }

            ConnectionStringSettings injectionValue = CreateValue();

            if (parameterType.Equals(typeof(String)))
            {
                return new InjectionParameter(parameterType, injectionValue.ConnectionString);
            }
            
            if (parameterType.Equals(typeof(ConnectionStringSettings)))
            {
                return new InjectionParameter(parameterType, injectionValue);
            }

            String message = String.Format(CultureInfo.InvariantCulture, Resources.ConnectionStringParameterValueElement_InvalidParameterType, parameterType.FullName);

            throw new InvalidOperationException(message);
        }

        [ConfigurationProperty("connectionStringKey", IsRequired = true)]
        public String ConnectionStringKey
        {
            get
            {
                return (String)base["connectionStringKey"];
            }

            set
            {
                base["connectionStringKey"] = value;
            }
        }

        private IConfigurationStore Config
        {
            get;
            set;
        }
    }
}

Usually a string parameter would be used as the injection type. This will then decouple the dependency from the System.Configuration assembly. The only time that the ConnectionStringSettings class should be used as the injection parameter type is when the provider information on the connection string configuration is required for some application logic.

The Unity configuration needs to be set up with the SectionExtensionInitiator class to support connection string injection. A connectionString element can then be used to define a connection string injection value.

<?xml version="1.0"
      encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity"
             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
  </configSections>
  <connectionStrings>
    <add name="TestConnection" connectionString="Data Source=localhost;Database=SomeDatabase;Integrated Security=SSPI;"/>
  </connectionStrings>
  <unity>
    <sectionExtension type="Neovolve.Toolkit.Unity.SectionExtensionInitiator, Neovolve.Toolkit.Unity" />
    <containers>
      <container>
        <register type="Neovolve.Toolkit.Unity.IntegrationTests.IDoSomething, Neovolve.Toolkit.Unity.IntegrationTests"
                  mapTo="Neovolve.Toolkit.Unity.IntegrationTests.ConnectionTest, Neovolve.Toolkit.Unity.IntegrationTests"
                  name="ConnectionStringTesting">
          <constructor>
            <param name="connectionString">
              <connectionSetting connectionStringKey="TestConnection" />
            </param>
          </constructor>        
        </register>
      </container>
    </containers>
  </unity>
</configuration>

This configuration will resolve a connection string with the name TestConnection and inject it into the constructor of the ConnectionTest class.

All the code and documentation for this implementation is in my Toolkit project on CodePlex.

Tags:

Jun 28 2010

Unity dependency injection for ASP.Net MVC2

Category: .NetRory Primrose @ 08:34

I knew that I should address the popular MVC model for ASP.Net as soon as I had finished my post on Unity dependency injection for ASP.Net. A different implementation for Unity injection is required here as the MCV model has a different method of processing ASP.Net pages.

Like the posts for Unity injection with WCF and ASP.Net, there are similar implementations already published on the internet to support MVC. The main issues I have with the examples posted elsewhere are that:

  • global.asax application events are used which are not easily portable between projects
  • there is no support for teardown operations of the controllers created via Unity (especially important when Unity creates disposable build tree hierarchies)

ASP.Net MVC uses a controller factory to create controller instances. Only the controllers need to be created via Unity as the view is a UI control and the model should not have any logic. In a similar manner to the ASP.Net implementation, supporting MVC Unity injection leverages a HttpModule to configure the application for injection.

The UnityControllerFactoryHttpModule creates a new UnityControllerFactory if the current factory has not already been configured for Unity injection. It resets the controller factory back to a default factory when the module is disposed.

using System.Web;
using System.Web.Mvc;
using Microsoft.Practices.Unity;

namespace Neovolve.Toolkit.Unity
{
    public class UnityControllerFactoryHttpModule : UnityHttpModuleBase
    {
        public override void Init(HttpApplication context)
        {
            base.Init(context);

            UnityControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory() as UnityControllerFactory;

            if (controllerFactory != null)
            {
                return;
            }

            UnityControllerFactory factory = new UnityControllerFactory(Container);

            ControllerBuilder.Current.SetControllerFactory(factory);
        }

        public override void Dispose()
        {
            IControllerFactory defaultFactory = new DefaultControllerFactory();

            ControllerBuilder.Current.SetControllerFactory(defaultFactory);

            base.Dispose();
        }
    }
}

The UnityControllerFactoryHttpModule uses a base class refactored from the original ASP.Net implementation. This base class is responsible for resolving and disposing the Unity container for the module. The class calls into a UnityContainerResolver helper class to resolve the unity container. The code for UnityContainerResolver can be found here.

using System;
using System.Diagnostics.Contracts;
using System.Web;
using System.Web.UI;
using Microsoft.Practices.Unity;

namespace Neovolve.Toolkit.Unity
{
    public abstract class UnityHttpModuleBase : IHttpModule
    {
        private static readonly Object SyncLock = new Object();

        private static IUnityContainer _container;

        public virtual void Dispose()
        {
            DestroyContainer();
        }

        public virtual void Init(HttpApplication context)
        {
            // The container should only be assigned once by the Init method of the module
            // There are a pool of modules against the app pool and we only want the container created once
            AssignContainer(UnityContainerResolver.Resolve, false);
        }

        protected static void AssignContainer(Func<IUnityContainer> getContainer, Boolean allowContainerReassignment)
        {
            Contract.Requires<ArgumentNullException>(getContainer != null, "No getContainer function was provided");
            Contract.Ensures(_container != null, "Container was not created");

            if (allowContainerReassignment == false && _container != null)
            {
                return;
            }

            lock (SyncLock)
            {
                // Protect the container against multiple threads and instances that get past the initial check
                if (allowContainerReassignment == false && _container != null)
                {
                    return;
                }

                _container = getContainer();
            }
        }

        private static void DestroyContainer()
        {
            Contract.Ensures(_container == null, "Container was not destroyed");

            if (_container == null)
            {
                return;
            }

            lock (SyncLock)
            {
                // Protect the container against multiple threads and instances that get past the initial check
                if (_container == null)
                {
                    return;
                }

                _container.Dispose();
                _container = null;
            }
        }

        public static IUnityContainer Container
        {
            get
            {
                return _container;
            }

            set
            {
                // This is a manual container assignment so it can overwrite the existing container
                AssignContainer(() => value, true);
            }
        }
    }
}

The UnityControllerFactory class is used to create and teardown controller instances. This is the class that is the bridge between ASP.Net MVC and a Unity container. The GetControllerInstance method is a copy of the method in the base class with the only change being that the instance is resolved from Unity rather than via Activator.CreateInstance().

The factory uses the container to tear down the controller instance when the application notifies the factory that it is finished with it. The base class method is also invoked to cover the case when the container isn’t configured for tear down operations. This ensures that at least the controller instance itself can be disposed if it implements IDisposable. I suggest you configure the container to teardown build trees with my custom extension as this will ensure that all disposable instances are correctly destroyed.

using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using Neovolve.Toolkit.Unity.Properties;

namespace Neovolve.Toolkit.Unity
{
    internal class UnityControllerFactory : DefaultControllerFactory
    {
        public UnityControllerFactory(IUnityContainer container)
        {
            Contract.Requires<ArgumentNullException>(container != null, "No container has been provided.");

            Container = container;
        }

        public override void ReleaseController(IController controller)
        {
            Container.Teardown(controller);

            base.ReleaseController(controller);
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            Debug.Assert(requestContext != null, "No requestContext was provided");

            IController controller;

            if (controllerType == null)
            {
                String message = String.Format(
                    CultureInfo.CurrentUICulture, Resources.UnityControllerFactory_NoControllerFound, requestContext.HttpContext.Request.Path);

                throw new HttpException(404, message);
            }

            if (typeof(IController).IsAssignableFrom(controllerType) == false)
            {
                String message = String.Format(
                    CultureInfo.CurrentUICulture, Resources.UnityControllerFactory_TypeDoesNotSubclassControllerBase, controllerType);

                throw new ArgumentException(message, "controllerType");
            }

            try
            {
                controller = (IController)Container.Resolve(controllerType);
            }
            catch (Exception exception)
            {
                String message = String.Format(CultureInfo.CurrentUICulture, Resources.UnityControllerFactory_ErrorCreatingController, controllerType);

                throw new InvalidOperationException(message, exception);
            }

            return controller;
        }

        protected IUnityContainer Container
        {
            get;
            private set;
        }
    }
}

Configuring this module is similar to the ASP.Net implementation. The following example hooks up the UnityControllerFactoryHttpModule and the DisposableStrategyExtension and configures injection support for the HashAlgorithm type.

<?xml version="1.0" ?>
<configuration>
    <configSections>
        <section name="unity"
                 type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
    </configSections>
    <unity>
        <containers>
            <container>
                <register type="System.Security.Cryptography.HashAlgorithm, mscorlib"
                          mapTo="System.Security.Cryptography.SHA256CryptoServiceProvider, System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
                <extensions>
                    <add type="Neovolve.Toolkit.Unity.DisposableStrategyExtension, Neovolve.Toolkit.Unity"/>
                </extensions>
            </container>
        </containers>
    </unity>
    <system.web>
        <compilation debug="true"
                     targetFramework="4.0"/>
        <authentication mode="None"></authentication>
        <httpModules>
            <add type="Neovolve.Toolkit.Unity.UnityControllerFactoryHttpModule"
                 name="UnityControllerFactoryHttpModule"/>
        </httpModules>
    </system.web>
    <system.webServer>
        <validation validateIntegratedModeConfiguration="false"/>
        <modules runAllManagedModulesForAllRequests="true">
            <add type="Neovolve.Toolkit.Unity.UnityControllerFactoryHttpModule"
                 name="UnityControllerFactoryHttpModule"/>
        </modules>
    </system.webServer>
</configuration>

This configuration can now be used to create any controller that has a HashAlgorithm type in its constructor.

using System;
using System.Security.Cryptography;
using System.Text;
using System.Web.Mvc;

namespace Neovolve.Toolkit.Unity.MvcWebIntegrationTests.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public HomeController(HashAlgorithm hashCalulator)
        {
            HashCalculator = hashCalulator;
        }

        public ActionResult About()
        {
            return View();
        }

        public ActionResult Index()
        {
            String valueToHash = Guid.NewGuid().ToString();
            Byte[] valueInBytes = Encoding.UTF8.GetBytes(valueToHash);
            Byte[] hashBytes = HashCalculator.ComputeHash(valueInBytes);

            String values = valueToHash + " - " + Convert.ToBase64String(hashBytes);

            ViewData["Message"] = "Welcome to ASP.NET MVC! " + values;

            return View();
        }

        protected HashAlgorithm HashCalculator
        {
            get;
            private set;
        }
    }
}

All the code for this solution (including full xml documentation) can be found in my Toolkit project on CodePlex.

Tags: , , ,

Jun 24 2010

Full support for custom types in TFS Build 2010 build definition editor

Category: .NetRory Primrose @ 08:01

I have been putting together a customised build process with TFS Build 2010. Several parts of the functionality added to the build process involve custom types that are used as arguments for the build definition. Deploy and configuration update information are some examples of this. I wanted to get the same designer experience out of the Process tab property grid that is available for properties like the test list definition.

The Automated Tests property seen here has a set of sub-items for each test item specified (items in the collection property). Each of these is expandable and the display text of each parent property is customised according to the values of the child properties.

I used the TestSpecList and TestSpec classes via Reflector as a template to determine what I needed to implement to get this support. It involved a combination of expandable object converters and type descriptors. It is not rocket science and happens to be what I was playing with about six years ago.

Unfortunately I just couldn’t get the same result for my custom types. My custom collection types for the build definition (Configuration Updates and Wix deployment list seen below) simply got displayed as (Collection).

 

The best I could do was to change the workflow argument from CollectionOfT to T[]. This would then display an expandable set of the items in the array. Each of these could then display their own display text by overriding ToString of the type in the array.

The disadvantage with this is that the property grid does not display multiple levels of expandable properties. This means you can’t edit the properties of an item in a list without going into the collection editor for the top level property. It also means that you don’t get direct access to the editor assigned to a child property.

I browsed the source for this property grid usage in the Process tab with a bit of Reflector surfing. The process tab property grid is assigned a Microsoft.TeamFoundation.Build.Controls.TeamBuildWorkflowDescriptor instance which is populated with properties, property values and property metadata. This is created by the Microsoft.TeamFoundation.Build.Controls.ControlProcessParameterPropertyGrid control which prepares all the property data from the build definition. This uses XAML serialization to figure out the property information for the build definition.

I was guessing that the reason my expandable object converters and type descriptors were not being picked up was because the property grid was being provided a dynamic type that had been interpreted from XAML serialization. This would mean that the type being displayed in the grid did not have the type converter or type descriptor definitions for the custom types and was only displaying a guess of the type in the grid.

It then occurred to me that the way to make this type available to the code that populates the property grid would be to place my custom assembly in the same location. I copied my assembly into the PrivateAssemblies directory under the Visual Studio installation and viola, I now have expandable collection items.

The Configuration Updates and Wix deployment list properties are now expandable to display the children which are also expandable. The WixProjectFile item selected in the screenshot has a custom editor associated with it and this can be used directly from the Process tab without having to go through the collection editor of the Wix deployment list property. Similarly, all the property values displayed are editable here and the description of the selected property appears at the bottom of the property grid.

Using expandable object converters and type descriptors makes the property grid much more powerful and easy to use. The simple act of placing the assembly in PrivateAssemblies is the way to open up all these possibilities.

Kudos to Jim Lamb and Ewald Hofman for their excellent posts on TFS Build 2010 customisation.

Tags: , ,

Jun 22 2010

WiX Heat extension to deploy web projects to the bin directory

Category: .NetRory Primrose @ 08:28

WiX has a great little utility called Heat.exe (formally Tallow). Heat is used to harvest WiX project references to determine their contents. The content harvested is grouped by Binaries, Satellites, Content, Source, Symbols and Documentation. The benefit this provides to WiX is that you do not need to manually define each item to include in the MSI from a referenced project. If the referenced project changes, such as the contents of a website project, then these updates will be automatically put into the MSI when it is compiled.

Take the following solution for example.

Heat generates a wxs file at compile time for each harvested project (found in obj\Debug or obj\Release).

The wxs file for each project contains the harvested groups and all the items found for those groups. Each of these groups is then listed in the Product.Generated.wsx file.

<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <ComponentGroup Id="Product.Generated">
      <ComponentGroupRef Id="Neovolve.Testing.Business.Binaries" />
      <ComponentGroupRef Id="Neovolve.Testing.Business.Content" />
      <ComponentGroupRef Id="Neovolve.Testing.Business.Satellites" />
      <ComponentGroupRef Id="Neovolve.Testing.DataAccess.Binaries" />
      <ComponentGroupRef Id="Neovolve.Testing.DataAccess.Content" />
      <ComponentGroupRef Id="Neovolve.Testing.DataAccess.Satellites" />
      <ComponentGroupRef Id="Neovolve.Testing.Website.Binaries" />
      <ComponentGroupRef Id="Neovolve.Testing.Website.Content" />
      <ComponentGroupRef Id="Neovolve.Testing.Website.Satellites" />
    </ComponentGroup>
  </Fragment>
</Wix>

Product.wxs references the Product.Generated groups in order to bring in each of these components that have been harvested from the referenced projects.

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="9be5cd9c-59fd-42bc-b26a-9adb277748fc" Name="Neovolve.Testing.Website.Deployment" Language="1033" Version="1.0.0.0" Manufacturer="Neovolve.Testing.Website.Deployment" UpgradeCode="93f93da8-dc45-440f-9281-e0c95ff0a047">
        <Package InstallerVersion="200" Compressed="yes" />

        <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLLOCATION" Name="Neovolve.Testing.Website.Deployment">
                    <!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
                    <!-- <Component Id="ProductComponent" Guid="3d82ebb4-534c-45a3-bb2a-b2549e34f324"> -->
                        <!-- TODO: Insert files, registry keys, and other resources here. -->
                    <!-- </Component> -->
                </Directory>
            </Directory>
        </Directory>

        <Feature Id="ProductFeature" Title="Neovolve.Testing.Website.Deployment" Level="1">
            <!-- TODO: Remove the comments around this ComponentRef element and the Component above in order to add resources to this installer. -->
            <!-- <ComponentRef Id="ProductComponent" /> -->
            
            <!-- Note: The following ComponentGroupRef is required to pull in generated authoring from project references. -->
            <ComponentGroupRef Id="Product.Generated" />
        </Feature>
    </Product>
</Wix>

The issue with web projects is that they have a different output model to other projects. The binaries (and satellite files) need to be written to the bin directory whereas all the other groups get pushed into the root directory. The issue with WiX is that Heat can only be told one location to put the files for a project reference via the Directory Id property.

As you can see here, the WiX will define that the groups found in the website project will be written to INSTALLLOCATION. The wxs file generated by Heat points all groups to that location.

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="cmpF5DE28615909286A5CF94B63995C6D55" Guid="*">
                <File Id="fil51E45E008FA549C8FEC2368DEF501052" Source="$(var.Neovolve.Testing.Website.TargetDir)\Neovolve.Testing.Website.dll" />
            </Component>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="Neovolve.Testing.Website.Binaries">
            <ComponentRef Id="cmpF5DE28615909286A5CF94B63995C6D55" />
        </ComponentGroup>
    </Fragment>
    <Fragment>
        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="cmp7447AC93EFB528AD42357B92D13DA248" Guid="*">
                <File Id="filB6E856E8FCE544386E258755DFA62FB2" Source="$(var.Neovolve.Testing.Website.TargetDir)\Neovolve.Testing.Website.pdb" />
            </Component>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="Neovolve.Testing.Website.Symbols">
            <ComponentRef Id="cmp7447AC93EFB528AD42357B92D13DA248" />
        </ComponentGroup>
    </Fragment>
    <Fragment>
        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="cmpBADFDF35D069BB612B0C36E0B91E0CEE" Guid="*">
                <File Id="fil7C5AF9F50D8F59F3001208BFF05BD9EC" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Default.aspx.cs" />
            </Component>
            <Component Id="cmpCA420CF266EE0AC6E6353B17282AFA0B" Guid="*">
                <File Id="filBE5A41EDAE3AD2CE56A8E144CF450A44" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Default.aspx.designer.cs" />
            </Component>
            <Component Id="cmp3B7EB2172A9FEBEA083DB686E3A685DA" Guid="*">
                <File Id="filF8E47D5133125739D9DB05BBE1CEE782" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Neovolve.Testing.Website.csproj" />
            </Component>
            <Directory Id="dirC8411BF1562B264784B7DF0627B19B93" Name="Properties">
                <Component Id="cmp6BDA64642971F4B5910D37B5EA49D5A6" Guid="*">
                    <File Id="fil5861A17AB40D6B9D2E8AE31ACD735043" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Properties\AssemblyInfo.cs" />
                </Component>
            </Directory>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="Neovolve.Testing.Website.Sources">
            <ComponentRef Id="cmpBADFDF35D069BB612B0C36E0B91E0CEE" />
            <ComponentRef Id="cmpCA420CF266EE0AC6E6353B17282AFA0B" />
            <ComponentRef Id="cmp3B7EB2172A9FEBEA083DB686E3A685DA" />
            <ComponentRef Id="cmp6BDA64642971F4B5910D37B5EA49D5A6" />
        </ComponentGroup>
    </Fragment>
    <Fragment>
        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="cmp122BECA26B8394BF625F932899B0A96B" Guid="*">
                <File Id="fil0BDB8FF098B6E41D5C4392D5D9403EC6" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Default.aspx" />
            </Component>
            <Component Id="cmp317A8DE3944BF93D29FBA73385C7FB3C" Guid="*">
                <File Id="fil2AA289B048C9CB1673EB85175274E9A7" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Web.config" />
            </Component>
            <Component Id="cmpFFE68B22B3EF3B6AD1EAA3654AAD8CB6" Guid="*">
                <File Id="fil7A038E012724A00962169C23F4D3158B" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Web.Debug.config" />
            </Component>
            <Component Id="cmpBFD25437421ACC4246FDBC9C2CB1D3A6" Guid="*">
                <File Id="filA6CE0BCA1DD718573234014E6E2F2945" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Web.Release.config" />
            </Component>
            <Directory Id="dir60CCC7F604F341FEF1A6E222A97C4319" Name="Scripts">
                <Component Id="cmpCE48EDC196DDDD38F8AB29828A86E7F3" Guid="*">
                    <File Id="filD1C3DAD47E6A3C50AAA61D862AEEE443" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Scripts\jquery-1.4.1-vsdoc.js" />
                </Component>
                <Component Id="cmpFB9055B4DA9F2CFF916FBF3CD5219635" Guid="*">
                    <File Id="filDB215DE9951F6E985AC917B9AE075EB0" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Scripts\jquery-1.4.1.js" />
                </Component>
                <Component Id="cmp3E6685FA6EB5434F852D1B4DA48980BF" Guid="*">
                    <File Id="fil051C169AB656BE24EDE133EAFEC885B6" Source="$(var.Neovolve.Testing.Website.ProjectDir)\Scripts\jquery-1.4.1.min.js" />
                </Component>
            </Directory>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="Neovolve.Testing.Website.Content">
            <ComponentRef Id="cmp122BECA26B8394BF625F932899B0A96B" />
            <ComponentRef Id="cmp317A8DE3944BF93D29FBA73385C7FB3C" />
            <ComponentRef Id="cmpFFE68B22B3EF3B6AD1EAA3654AAD8CB6" />
            <ComponentRef Id="cmpBFD25437421ACC4246FDBC9C2CB1D3A6" />
            <ComponentRef Id="cmpCE48EDC196DDDD38F8AB29828A86E7F3" />
            <ComponentRef Id="cmpFB9055B4DA9F2CFF916FBF3CD5219635" />
            <ComponentRef Id="cmp3E6685FA6EB5434F852D1B4DA48980BF" />
        </ComponentGroup>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="Neovolve.Testing.Website.Satellites" />
    </Fragment>
    <Fragment>
        <ComponentGroup Id="Neovolve.Testing.Website.Documents" />
    </Fragment>
</Wix>

This means that binaries and satellite files will get installed to the root directory of a website rather than into the bin directory.

The fix to this issue is to write a Heat extension that will redirect Binaries and Satellites groups to another location at compile time. Heat is extensible so that a new harvester can be defined to provide particular harvesting functionality.

The first task is to create the extension project by adding a new library project. It must reference wix.dll. This extension will hijack the operations of VSProjectHarvester so WixVSExtension.dll also needs to be referenced.

Heat uses an assembly attribute to indicate the heat extension type so this must be added to the AssemblyInfo.cs.

[assembly: AssemblyDefaultHeatExtension(typeof(VsWebProjectHeatExtension))]

The VsWebProjectHeatExtension checks to see if the VSProjectHarvester is already assigned as the harvester to use for the current project processed by Heat.

using System;
using System.Reflection;
using Microsoft.Tools.WindowsInstallerXml.Extensions;
using Microsoft.Tools.WindowsInstallerXml.Tools;

namespace Neovolve.WixExtensions
{
    public class VsWebProjectHeatExtension : HeatExtension
    {
        public override void ParseOptions(String type, String[] args)
        {
            VSProjectHarvester harvester = Core.Harvester.Extension as VSProjectHarvester;

            if (harvester != null)
            {
                // Hijack this harvester
                VsWebProjectHarvester webHarvester = new VsWebProjectHarvester(harvester);
                FieldInfo field = Core.Harvester.GetType().GetField(
                    "harvesterExtension", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.SetField | BindingFlags.Instance);

                field.SetValue(Core.Harvester, webHarvester);
            }
        }

        public override HeatCommandLineOption[] CommandLineTypes
        {
            get
            {
                return new HeatCommandLineOption[0];
            }
        }
    }
}

This is where things get a little hacky. While Heat is extensible, it doesn’t adhere to the Open/Closed principle as almost everything is sealed and isn’t open to extension. The extensibility in Heat is really only there to provide new functionality and doesn’t allow you to extend existing functionality. In this case, Core.Harvester throws an exception if trying to assign a harvester when one has already been assigned. This is an issue because I still want VSProjectHarvester to do it’s work, I just want to modify its output. The hack around this is to use reflection to force my custom harvester into the field behind Core.Harvester.

The VsWebProjectHarvester class calls down to the original VSProjectHarvester to generate all the fragments for the harvested items. If the project harvested is a web project (determined via the project type guid), then the fragments are modified to direct binaries and satellite files to a hard coded directory with the Id of BINLOCATION.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Microsoft.Tools.WindowsInstallerXml;
using Microsoft.Tools.WindowsInstallerXml.Extensions;
using Microsoft.Tools.WindowsInstallerXml.Serialize;

namespace Neovolve.WixExtensions
{
    public class VsWebProjectHarvester : HarvesterExtension
    {
        public const String WebProjectTypeGuidValue = "{349c5851-65df-11da-9384-00065b846f21}";

        public VsWebProjectHarvester(VSProjectHarvester harvester)
        {
            ProjectHarvester = harvester;
        }

        public override Fragment[] Harvest(String argument)
        {
            ProjectHarvester.Core = Core;

            Fragment[] fragments = ProjectHarvester.Harvest(argument);

            if (IsWebProject(argument) == false)
            {
                return fragments;
            }

            foreach (Fragment fragment in fragments)
            {
                ComponentGroup binariesGroup = fragment.Children.OfType<ComponentGroup>().FirstOrDefault(
                    child => child.Id != null && (child.Id.EndsWith(".Binaries", StringComparison.InvariantCulture) || child.Id.EndsWith(".Satellites", StringComparison.InvariantCulture)));

                if (binariesGroup == null)
                {
                    continue;
                }

                IEnumerable<ComponentRef> componentRefs = binariesGroup.Children.OfType<ComponentRef>();

                foreach (IEnumerable<DirectoryRef> directoryRefs in from componentReference in componentRefs
                                                                    from childFragment in fragments
                                                                    select
                                                                        childFragment.Children.OfType<DirectoryRef>().Where(
                                                                            x =>
                                                                            x.Children.OfType<Component>().Where(y => y.Id == componentReference.Id).
                                                                                FirstOrDefault() != null))
                {
                    directoryRefs.ToList().ForEach(x => x.Id = "BINLOCATION");
                }
            }

            return fragments;
        }

        private static Boolean IsWebProject(String filePath)
        {
            XmlDocument doc = new XmlDocument();

            doc.Load(filePath);

            XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);

            manager.AddNamespace("ns", "http://schemas.microsoft.com/developer/msbuild/2003");

            XmlNode projectTypeNode = doc.SelectSingleNode("//ns:Project/ns:PropertyGroup/ns:ProjectTypeGuids", manager);

            if (projectTypeNode == null)
            {
                return false;
            }

            String value = projectTypeNode.InnerText;

            if (value.IndexOf(WebProjectTypeGuidValue, StringComparison.InvariantCultureIgnoreCase) > -1)
            {
                return true;
            }

            return false;
        }

        protected VSProjectHarvester ProjectHarvester
        {
            get;
            set;
        }
    }
}

Heat now needs to be told about the extension. The extension assembly needs to be put into the same directory as Heat once it is built. The heat.exe.config then needs to be updated to include this extension.

<?xml version="1.0" encoding="utf-8"?>
<!--
    Copyright (c) Microsoft Corporation.  All rights reserved.
-->
<configuration>
    <appSettings>
        <add key="extensions" value="WixIIsExtension;WixUtilExtension;WixVSExtension;Neovolve.WixExtensions"/>
    </appSettings>
    <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0" />
        <supportedRuntime version="v2.0.50727" />
    </startup>
</configuration>

The heat extensions are processed in order so the custom Neovolve.WixExtensions assembly is defined last. Heat will loop through each extension to determine who is going to harvest a project. The WixVSExtension project will say that it will take care of the web project and its harvester will get assigned to Core.Harvester. The custom extension gets called next at which point the harvester is hijacked.

The next time you compile you will get an error saying

Unresolved reference to symbol 'Directory:BINLOCATION' in section 'Fragment’

So now the automatically generated wxs file for the web project points binaries and satellite files to the BINLOCATION directory. This now needs to be defined by adding a <Directory Id=”BINLOCATION” Name=”bin” /> under the INSTALLLOCATION Directory in Product.wxs. This defines the directory location for the website binaries to be written to.

The Product.wxs now looks like the following:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="9be5cd9c-59fd-42bc-b26a-9adb277748fc" Name="Neovolve.Testing.Website.Deployment" Language="1033" Version="1.0.0.0" Manufacturer="Neovolve.Testing.Website.Deployment" UpgradeCode="93f93da8-dc45-440f-9281-e0c95ff0a047">
        <Package InstallerVersion="200" Compressed="yes" />

        <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLLOCATION" Name="Neovolve.Testing.Website.Deployment">

          <Directory Id="BINLOCATION" Name="bin" />          
          
                    <!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
                    <!-- <Component Id="ProductComponent" Guid="3d82ebb4-534c-45a3-bb2a-b2549e34f324"> -->
                        <!-- TODO: Insert files, registry keys, and other resources here. -->
                    <!-- </Component> -->
                </Directory>
            </Directory>
        </Directory>

        <Feature Id="ProductFeature" Title="Neovolve.Testing.Website.Deployment" Level="1">
            <!-- TODO: Remove the comments around this ComponentRef element and the Component above in order to add resources to this installer. -->
            <!-- <ComponentRef Id="ProductComponent" /> -->
            
            <!-- Note: The following ComponentGroupRef is required to pull in generated authoring from project references. -->
            <ComponentGroupRef Id="Product.Generated" />
        </Feature>
    </Product>
</Wix>

The last step is dealing with the other project dependencies. The properties of all other projects referenced by the website project are also added as Wix project references. These now need to be pointed to BINLOCATION as well. Note that the website project is still deployed to INSTALLLOCATION as the heat extension hijacks just the Binaries and Satellite groups and redirects them to BINLOCATION at compile time.

The project reference properties for this example now look like the following.

The compiled MSI will now direct all binaries to the bin directory and all website contents to the root installation directory.

Tags: ,

Jun 18 2010

Unity Extension For Disposing Build Trees On TearDown

Category: .NetRory Primrose @ 08:40

A Unity container is used to create objects for use by an application. There are several reasons why it is also responsible for cleaning up the instances that it creates.

  • A Unity container owns the instances according to my Law Of Instance Ownership as the container would typically be near the top of the call stack and higher stack frames don’t hold a reference to container created instances
  • Container management in a WCF Service (see here and here) or an ASP.Net application (see here) result in the container being held outside of the scope of the application code
  • The container is already responsible for the lifetime management of instances with respect to instance creation

Code outside the container should not be concerned with the lifetime management of instances created by the container for these reasons.

The IUnityContainer interface in Unity provides a TearDown method which can be used to clean up container instances. TearDown invokes the IBuilderStrategy.PreTearDown and IBuilderStrategy.PostTearDown methods on each builder strategy added to the container. Unfortunately the TearDown method has no affect by default as none of the “out of the box” strategies dispose created instances.

This can be addressed by creating a builder strategy that is hooked up via a Unity extension. This extension will allow for IDisposable instances to be disposed by the container when TearDown is invoked.

My initial version of this implementation was very simple.

using System;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace Neovolve.Toolkit.Unity
{
    public class DisposableStrategyExtension : UnityContainerExtension
    {
        protected override void Initialize()
        {
            Context.Strategies.Add(new DisposableBuilderStrategy(), UnityBuildStage.PostInitialization);
        }
    }

    internal class DisposableBuilderStrategy : BuilderStrategy
    {
        public override void PostTearDown(IBuilderContext context)
        {
            base.PostTearDown(context);

            IDisposable disposable = context.Existing as IDisposable;

            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
   }
}

Unfortunately this implementation failed miserably. Only the instance passed into the TearDown method was disposed rather than the instance and all its dependencies. It turns out that UnityContainer constructs an IBuilderContext with the instance provided to TearDown regardless of whether the instance was resolved or built up by the container. A variation on this method is to use property reflection on the instance in order to identify its dependencies.

Both of this implementation and the variation suffer from the same problems.

  1. The instance passed into TearDown may not have been created by the container (consider a ASP.Net page that has been BuildUp by the container – we can’t dispose this)
  2. The instance or any of its dependencies may exist in a lifetime manager for reuse in subsequent Resolve/BuildUp operations
  3. Dependencies injected via constructor that are not visible via properties (or even reflected fields) will not be disposed
  4. Instances that have been de-referenced can no longer be accessed
  5. Properties that had an instance injected may have since had a new instance assigned to it
  6. Instances may have a hierarchy of dependencies so recursion is required to dispose more than the first level dependencies

Unfortunately Unity containers do not track the instances they create. There is no stored knowledge in the container of the instances or the relationships between them. The only time that a container holds on to an instance is in a lifetime manager. In this case, the lifetime manager returns the instance to use on Resolve or BuildUp rather than creating a new instance. Typical usages of the lifetime manager are to reuse instances as singletons, unique to threads or unique to Resolve/BuildUp operations.

A builder strategy that will address all these concerns will need to track build trees as they are created. It can then use that knowledge to dispose build trees when an instance is provided to TearDown. An example of a build tree might be:

  • ServiceLayer
    • SecuritySlice
      • Logger
      • AuditSlice
        • Auditer
        • BusinessLayer
          • Logger
          • ServiceDependency
            • Logger
          • DataCacheSlice
            • Logger
            • DataLayer
              • DatabaseAccess
                • Logger
              • Logger

Tree nodes must use a WeakReference for the tracked instance. This ensures that the build tree does not prevent the instance from being garbage collected while the build tree is still rooted in memory. Consider an instance for a tree node which is nulled out or replaced with another instance in application code before garbage collection occurs. The garbage collector would not be able to clean up the instance for the tree node if the build tree still held a direct reference to it even though application code no longer does. This is important because created instances can go out of scope in application code at any time regardless of if/when TearDown is invoked.

The benefit of tracking and storing a build tree means that if dependencies are de-referenced (meaning a sub tree of instances is orphaned from its parent instance), the TearDown of the build tree would still track the references to the orphaned instances and they can still be disposed. The same benefit also applies to instances injected into a constructor that may not be made available via a property or field on the parent instance.

The Code

Build trees are made up build tree nodes which may have 0-many children and a reference back to their parent.

using System;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using Microsoft.Practices.ObjectBuilder2;
using Neovolve.Toolkit.Unity.Properties;

namespace Neovolve.Toolkit.Unity
{
    internal class BuildTreeItemNode
    {
        public BuildTreeItemNode(
            NamedTypeBuildKey buildKey, Boolean nodeCreatedByContainer, BuildTreeItemNode parentNode)
        {
            Contract.Requires<ArgumentNullException>(buildKey != null);

            BuildKey = buildKey;
            NodeCreatedByContainer = nodeCreatedByContainer;
            Parent = parentNode;
            Children = new Collection<BuildTreeItemNode>();
        }

        public void AssignInstance(Object instance)
        {
            if (ItemReference != null)
            {
                throw new ArgumentException(Resources.BuildTreeNode_InstanceAlreadyAssigned_ExceptionMessage, "instance");
            }

            ItemReference = new WeakReference(instance);
        }

        public NamedTypeBuildKey BuildKey
        {
            get;
            private set;
        }

        public Collection<BuildTreeItemNode> Children
        {
            get;
            private set;
        }

        public WeakReference ItemReference
        {
            get;
            private set;
        }

        public Boolean NodeCreatedByContainer
        {
            get;
            private set;
        }

        public BuildTreeItemNode Parent
        {
            get;
            private set;
        }
    }
}

The DisposableStrategyExtension is responsible for attaching the build tree tracker strategy and for disposing all the build trees when the owning container is disposed.

using System;
using System.Diagnostics.Contracts;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace Neovolve.Toolkit.Unity
{
    public class DisposableStrategyExtension : UnityContainerExtension, IDisposable
    {
        public DisposableStrategyExtension()
            : this(new BuildTreeTracker())
        {
        }

        internal DisposableStrategyExtension(IBuildTreeTracker buildTreeTracker)
        {
            Contract.Requires<ArgumentNullException>(buildTreeTracker != null, "The buildTreeTracker provided is null.");

            TreeTracker = buildTreeTracker;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(Boolean disposing)
        {
            if (disposing)
            {
                // Free managed resources
                TreeTracker.DisposeAllTrees();
            }

            // Free native resources if there are any.
        }

        protected override void Initialize()
        {
            Context.Strategies.Add(TreeTracker, UnityBuildStage.PreCreation);
        }

        private IBuildTreeTracker TreeTracker
        {
            get;
            set;
        }
    }
}

The BuildTreeTracker is responsible for creating a build tree in a Resolve or BuildUp operation. This must detect the difference between a Resolve and a BuildUp as this will determine whether the root instance is disposed.

The tracker will find a build tree for an instance provided to TearDown and run a top down recursive dispose operation against the build tree. It will ignore nodes in the tree that were not created by the container or have been garbage collected and skip over nodes that exist in a lifetime manager. Once a build tree has been disposed, the tracker will also look for build trees that have root instances that have been garbage collected and dispose those build trees as well. Any build tree that the tracker has disposed are then removed to minimize memory usage.

The tracker uses a ThreadStatic to track the current node being built in a build tree as the container may be creating multiple build trees over several threads at the same time. Similarly the tracker needs to protect the list of build trees with suitable locking. The locking in this case uses my LockReader and LockWriter classes.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.Practices.ObjectBuilder2;
using Neovolve.Toolkit.Threading;

namespace Neovolve.Toolkit.Unity
{
    internal class BuildTreeTracker : BuilderStrategy, IBuildTreeTracker
    {
        private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

        private readonly List<BuildTreeItemNode> _buildTrees = new List<BuildTreeItemNode>();

        [ThreadStatic]
        private static BuildTreeItemNode _currentBuildNode;

        public void AssignInstanceToCurrentTreeNode(NamedTypeBuildKey buildKey, Object instance)
        {
            if (CurrentBuildNode.BuildKey != buildKey)
            {
                const String ErrorMessageFormat =
                    "Build tree constructed out of order. Build key '{0}' was expected but build key '{1}' was provided.";
                String message = String.Format(CultureInfo.CurrentCulture, ErrorMessageFormat, CurrentBuildNode.BuildKey, buildKey);

                throw new InvalidOperationException(message);
            }

            CurrentBuildNode.AssignInstance(instance);
        }

        public void DisposeAllTrees()
        {
            using (new LockReader(_lock, true))
            {
                for (Int32 index = BuildTrees.Count - 1; index >= 0; index--)
                {
                    BuildTreeItemNode buildTree = BuildTrees[index];

                    DisposeTree(null, buildTree);
                }
            }
        }

        public BuildTreeItemNode GetBuildTreeForInstance(Object instance)
        {
            using (new LockReader(_lock))
            {
                return BuildTrees.Where(x => x.ItemReference.IsAlive && ReferenceEquals(x.ItemReference.Target, instance)).SingleOrDefault();
            }
        }

        public override void PostBuildUp(IBuilderContext context)
        {
            if (context != null)
            {
                AssignInstanceToCurrentTreeNode(context.BuildKey, context.Existing);

                BuildTreeItemNode parentNode = CurrentBuildNode.Parent;

                if (parentNode == null)
                {
                    // This is the end of the creation of the root node
                    using (new LockWriter(_lock))
                    {
                        BuildTrees.Add(CurrentBuildNode);
                    }
                }

                // Move the current node back up to the parent
                // If this is the top level node, this will set the current node back to null
                CurrentBuildNode = parentNode;
            }

            base.PostBuildUp(context);
        }

        public override void PostTearDown(IBuilderContext context)
        {
            base.PostTearDown(context);

            // Get the build tree for this item
            if (context != null)
            {
                BuildTreeItemNode buildTree = GetBuildTreeForInstance(context.Existing);

                if (buildTree != null)
                {
                    DisposeTree(context, buildTree);
                }

                DisposeDeadTrees(context);
            }
        }

        public override void PreBuildUp(IBuilderContext context)
        {
            base.PreBuildUp(context);

            if (context != null)
            {
                Boolean nodeCreatedByContainer = context.Existing == null;

                BuildTreeItemNode newTreeNode = new BuildTreeItemNode(
                    context.BuildKey, nodeCreatedByContainer, CurrentBuildNode);

                if (CurrentBuildNode != null)
                {
                    // This is a child node
                    CurrentBuildNode.Children.Add(newTreeNode);
                }

                CurrentBuildNode = newTreeNode;
            }
        }

        private void DisposeDeadTrees(IBuilderContext context)
        {
            // Need to enumerate in the reverse order because the trees that are torn down are removed from the set
            using (new LockReader(_lock, true))
            {
                for (Int32 index = BuildTrees.Count - 1; index >= 0; index--)
                {
                    BuildTreeItemNode buildTree = BuildTrees[index];

                    if (buildTree.ItemReference.IsAlive == false)
                    {
                        DisposeTree(context, buildTree);
                    }
                }
            }
        }

        private void DisposeTree(IBuilderContext context, BuildTreeItemNode buildTree)
        {
            BuildTreeDisposer.DisposeTree(context, buildTree);

            using (new LockWriter(_lock))
            {
                BuildTrees.Remove(buildTree);
            }
        }

        private static BuildTreeItemNode CurrentBuildNode
        {
            get
            {
                return _currentBuildNode;
            }

            set
            {
                _currentBuildNode = value;
            }
        }

        public virtual IList<BuildTreeItemNode> BuildTrees
        {
            get
            {
                return _buildTrees;
            }
        }
    }
}

The BuildTreeTracker calls out to a helper class that is used to dispose build trees.

using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.Practices.ObjectBuilder2;

namespace Neovolve.Toolkit.Unity
{
    internal static class BuildTreeDisposer
    {
        public static void DisposeTree(IBuilderContext context, BuildTreeItemNode buildTree)
        {
            TeardownTreeNode(context, buildTree);
        }

        private static bool CanTeardownInstance(IBuilderContext context, WeakReference instanceReference)
        {
            return CanTeardownInstance(context, instanceReference.Target);
        }

        private static bool CanTeardownInstance(IBuilderContext context, Object instance)
        {
            if (InstanceExistsInLifetimeManager(context, instance))
            {
                // This instance is still stored in the Unity container for future reference
                return false;
            }

            return true;
        }

        private static void DisposeInstance(WeakReference instanceReference)
        {
            if (instanceReference.IsAlive)
            {
                DisposeInstance(instanceReference.Target);
            }
        }

        private static void DisposeInstance(Object instance)
        {
            IDisposable disposable = instance as IDisposable;

            if (disposable != null)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (ObjectDisposedException)
                {
                    Debug.WriteLine("Object was already disposed");
                }
            }
        }

        private static Boolean InstanceExistsInLifetimeManager(IBuilderContext context, Object instance)
        {
            if (context == null)
            {
                return false;
            }

            return context.Lifetime.OfType<ILifetimePolicy>().Any(lifetimeManager => ReferenceEquals(lifetimeManager.GetValue(), instance));
        }

        private static void TeardownTreeNode(IBuilderContext context, BuildTreeItemNode treeNode)
        {
            // If the parent node can't be torn down then neither can any of the children
            if (CanTeardownInstance(context, treeNode.ItemReference) == false)
            {
                return;
            }

            // Only nodes created by the unit container will be disposed
            if (treeNode.NodeCreatedByContainer)
            {
                DisposeInstance(treeNode.ItemReference);
            }

            // Recursively call through the child nodes
            for (Int32 index = 0; index < treeNode.Children.Count; index++)
            {
                BuildTreeItemNode child = treeNode.Children[index];

                TeardownTreeNode(context, child);
            }

            return;
        }
    }
}

Lastly there is the code to hook up the extension.

public void ExampleExtensionUsage()
{
    IDisposableType actual;

    using (UnityContainer container = new UnityContainer())
    {
        using (DisposableStrategyExtension disposableStrategyExtension = new DisposableStrategyExtension())
        {
            container.AddExtension(disposableStrategyExtension);
            container.RegisterType(typeof(IDisposableType), typeof(DisposableType), new TransientLifetimeManager());

            actual = container.Resolve<IDisposableType>();

            container.Teardown(actual);
        }
    }
}

This can also be done via configuration.

<?xml version="1.0" ?>
<configuration>
    <configSections>
        <section name="unity"
                 type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
    </configSections>
    <unity>
        <containers>
            <container>
                <register type="System.Security.Cryptography.HashAlgorithm, mscorlib"
                          mapTo="System.Security.Cryptography.SHA256CryptoServiceProvider, System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <extensions>
          <add type="Neovolve.Toolkit.Unity.DisposableStrategyExtension, Neovolve.Toolkit.Unity" />
        </extensions>
      </container>
        </containers>
    </unity>
</configuration>

All the code (including help documentation) can be found in my Toolkit project on CodePlex.

Tags: , ,