Auto-generating XML serialization classes from BizTalk schemas

August 28th, 2010 Rupert Benbrook No comments

When building BizTalk applications you often need to generate the XML serialization classes for your schemas. Sometimes this is a more efficient and simple way gain access to data and manipulate or construct complex messages than maps, distinguished fields, or XPath expressions. The normal way to achieve this is to run the XML Schema Definition tool (XSD.exe) on your schemas to generate the classes that represent them. However, this means leaving the comfort of the Visual Studio environment for the Command Prompt, and during the early phases of projects, when schemas are often in a state of flux, this becomes a right hassle. Believe me, the last thing you want to happen in a team of BizTalk developers is for the schemas and their serialization classes to get out of step!

I’ve been looking at different ways to solve this problem, and at first I started with MSBuild calling out to XSD.exe. This approach wasn’t very satisfactory as it was a bit clunky and required editing project files and the like. So I took a different tack and decided to use a T4 template.

If you haven’t come across T4 templates in Visual Studio then they’re probably it’s best kept secret. T4 actually stands for Text Template Transformation Toolkit, and it’s a very powerful code generation tool. I won’t spend time describing it in detail in this post as Scott Hanselman has a good introduction on his blog, and Oleg Synch has a great set of tutorial articles on it. Put simply, T4 lets me write some code that will generate some code for me, in this case the XML serialization classes for some schemas.

The first step was to work out how to do the same job as XSD.exe, but from code. Fortunately Mike Hadlow had been there before me. Next, I needed to put all this in a template, but I wanted to go a step further than just creating the XML serialization classes; I also wanted to generate serialize and deserialize methods for each of the root elements in each of the schemas. To do this I’d need to create multiple output files from T4, something it doesn’t do normally. Again, someone has been here before, this time Damien Guard with his excellent T4 Manager class. Now, armed with all this I could put the template together, and here it is:

<#@ template language="C#v3.5" hostSpecific="true" debug="false" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Linq" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System" #>
<#@ import namespace="System.CodeDom" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Xml.Serialization" #>
<#@ import namespace="System.Xml.Schema" #>
<#@ import namespace="Microsoft.CSharp" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<# var manager = Manager.Create(Host, GenerationEnvironment); #>
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:<#=Environment.Version.ToString()#>
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
//
// This source code was auto-generated by XmlSerializer.tt.
//
<#
	IServiceProvider hostServiceProvider = (IServiceProvider)Host;
	EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
	EnvDTE.ProjectItem templateProjectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
	EnvDTE.Project project = templateProjectItem.ContainingProject;
	XmlSchemas xsds = new XmlSchemas();
	foreach (EnvDTE.ProjectItem projectItem in GetAllItems(project.ProjectItems.Cast<EnvDTE.ProjectItem>()))
	{
		string path = projectItem.get_FileNames(0);
		string directory = Path.GetDirectoryName(path);
		if (path.EndsWith(".xsd"))
		{
			using (FileStream stream = File.OpenRead(path))
			{
				XmlSchema xsd = XmlSchema.Read(stream, null);
				xsds.Add(xsd);
				foreach(XmlSchemaElement schemaElement in xsd.Elements.Values)
				{
					manager.StartNewFile(schemaElement.Name + ".Serialization.cs");
#>
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace <#= project.Properties.Item("DefaultNamespace").Value.ToString() #>
{
	public partial class <#= schemaElement.Name #>
	{
		public static <#= schemaElement.Name #> Deserialize(Stream stream)
		{
			var serializer = new XmlSerializer(typeof(<#= schemaElement.Name #>));
			return (<#= schemaElement.Name #>)serializer.Deserialize(stream);
		}
		public static <#= schemaElement.Name #> Deserialize(TextReader reader)
		{
			var serializer = new XmlSerializer(typeof(<#= schemaElement.Name #>));
			return (<#= schemaElement.Name #>)serializer.Deserialize(reader);
		}
		public static <#= schemaElement.Name #> Deserialize(XmlReader reader)
		{
			var serializer = new XmlSerializer(typeof(<#= schemaElement.Name #>));
			return (<#= schemaElement.Name #>)serializer.Deserialize(reader);
		}
		public void Serialize(Stream stream)
		{
			var serializer = new XmlSerializer(typeof(<#= schemaElement.Name #>));
			serializer.Serialize(stream, this);
		}
		public void Serialize(TextWriter writer)
		{
			var serializer = new XmlSerializer(typeof(<#= schemaElement.Name #>));
			serializer.Serialize(writer, this);
		}
		public void Serialize(XmlWriter writer)
		{
			var serializer = new XmlSerializer(typeof(<#= schemaElement.Name #>));
			serializer.Serialize(writer, this);
		}
	}
}
<#
					manager.EndBlock();
				}
			}
		}
	}
	xsds.Compile(null, true);
	XmlSchemaImporter schemaImporter = new XmlSchemaImporter(xsds);
	CodeNamespace codeNamespace = new CodeNamespace(project.Properties.Item("DefaultNamespace").Value.ToString());
	XmlCodeExporter codeExporter = new XmlCodeExporter(codeNamespace);
	List<XmlTypeMapping> maps = new List<XmlTypeMapping>();
	foreach (XmlSchema xsd in xsds)
	{
		foreach(XmlSchemaType schemaType in xsd.SchemaTypes.Values)
		{
			maps.Add(schemaImporter.ImportSchemaType(schemaType.QualifiedName));
		}
		foreach(XmlSchemaElement schemaElement in xsd.Elements.Values)
		{
			maps.Add(schemaImporter.ImportTypeMapping(schemaElement.QualifiedName));
		}
	}
	foreach(XmlTypeMapping map in maps)
	{
		codeExporter.ExportTypeMapping(map);
	}
	CodeGenerator.ValidateIdentifiers(codeNamespace);
	CSharpCodeProvider codeProvider = new CSharpCodeProvider();
	using(StringWriter writer = new StringWriter(GenerationEnvironment))
	{
		codeProvider.GenerateCodeFromNamespace(codeNamespace, writer, new CodeGeneratorOptions());
	}
	manager.Process(true);
#>
<#+
private IEnumerable<EnvDTE.ProjectItem> GetAllItems(IEnumerable<EnvDTE.ProjectItem> projectItems)
{
	return projectItems.Concat(projectItems.SelectMany(i => GetAllItems(i.ProjectItems.Cast<EnvDTE.ProjectItem>())));
}
// Manager class records the various blocks so it can split them up
// From http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited
class Manager {
    private class Block {
        public String Name;
        public int Start, Length;
    }
    private Block currentBlock;
    private List<Block> files = new List<Block>();
    private Block footer = new Block();
    private Block header = new Block();
    private ITextTemplatingEngineHost host;
    private StringBuilder template;
    protected List<String> generatedFileNames = new List<String>();
    public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
        return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
    }
    public void StartNewFile(String name) {
        if (name == null)
            throw new ArgumentNullException("name");
        CurrentBlock = new Block { Name = name };
    }
    public void StartFooter() {
        CurrentBlock = footer;
    }
    public void StartHeader() {
        CurrentBlock = header;
    }
    public void EndBlock() {
        if (CurrentBlock == null)
            return;
        CurrentBlock.Length = template.Length - CurrentBlock.Start;
        if (CurrentBlock != header && CurrentBlock != footer)
            files.Add(CurrentBlock);
        currentBlock = null;
    }
    public virtual void Process(bool split) {
        if (split) {
            EndBlock();
            String headerText = template.ToString(header.Start, header.Length);
            String footerText = template.ToString(footer.Start, footer.Length);
            String outputPath = Path.GetDirectoryName(host.TemplateFile);
            files.Reverse();
            foreach(Block block in files) {
                String fileName = Path.Combine(outputPath, block.Name);
                String content = headerText + template.ToString(block.Start, block.Length) + footerText;
                generatedFileNames.Add(fileName);
                CreateFile(fileName, content);
                template.Remove(block.Start, block.Length);
            }
        }
    }
    protected virtual void CreateFile(String fileName, String content) {
        if (IsFileContentDifferent(fileName, content))
            File.WriteAllText(fileName, content);
    }
    public virtual String GetCustomToolNamespace(String fileName) {
        return null;
    }
    public virtual String DefaultProjectNamespace {
        get { return null; }
    }
    protected bool IsFileContentDifferent(String fileName, String newContent) {
        return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
    }
    private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
        this.host = host;
        this.template = template;
    }
    private Block CurrentBlock {
        get { return currentBlock; }
        set {
            if (CurrentBlock != null)
                EndBlock();
            if (value != null)
                value.Start = template.Length;
            currentBlock = value;
        }
    }
    private class VSManager: Manager {
        private EnvDTE.ProjectItem templateProjectItem;
        private EnvDTE.DTE dte;
        private Action<String> checkOutAction;
        private Action<IEnumerable<String>> projectSyncAction;
        public override String DefaultProjectNamespace {
            get {
                return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
            }
        }
        public override String GetCustomToolNamespace(string fileName) {
            return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
        }
        public override void Process(bool split) {
            if (templateProjectItem.ProjectItems == null)
                return;
            base.Process(split);
            projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
        }
        protected override void CreateFile(String fileName, String content) {
            if (IsFileContentDifferent(fileName, content)) {
                CheckoutFileIfRequired(fileName);
                File.WriteAllText(fileName, content);
            }
        }
        internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
            : base(host, template) {
            var hostServiceProvider = (IServiceProvider) host;
            if (hostServiceProvider == null)
                throw new ArgumentNullException("Could not obtain IServiceProvider");
            dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE));
            if (dte == null)
                throw new ArgumentNullException("Could not obtain DTE from host");
            templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
            checkOutAction = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
            projectSyncAction = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
        }
        private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
            var keepFileNameSet = new HashSet<String>(keepFileNames);
            var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
            var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + ".";
            foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
                projectFiles.Add(projectItem.get_FileNames(0), projectItem);
            // Remove unused items from the project
            foreach(var pair in projectFiles)
                if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
                    pair.Value.Delete();
            // Add missing files to the project
            foreach(String fileName in keepFileNameSet)
                if (!projectFiles.ContainsKey(fileName))
                    templateProjectItem.ProjectItems.AddFromFile(fileName);
        }
        private void CheckoutFileIfRequired(String fileName) {
            var sc = dte.SourceControl;
            if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
                checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
        }
    }
}
#>

Initial projectsT4 template added with output filesFor simplicity’s sake I’ve merged Damien’s T4 Manager class into this template, just so there’s a single file to drop into the project. So, how do you use this template? Well first create a BizTalk project with some schemas, and then create a C# class library project alongside it; this project is going to contain the XML serialization classes. Next, add the schemas from the BizTalk project into the C# class library project, but as a link so they don’t get copied into the project, just referred to. Now just add the template, which I’ve named XmlSerializer.tt, to the project. This will automatically generate XmlSerializer.cs, which contains the XML serialization classes, and also *.Serialization.cs for each of the root elements in each of the schema files. It couldn’t be easier! There’s only one thing to watch out for; the code generation only occurs when you save the template file or select Run Custom Tool from its context menu in Solution Explorer. So if you change your schemas you’ll need to remember to resave the template to regenerate the serialization code. This is just how T4 works out of the box, but it’s an awful lot easier than getting out a Command Prompt and remembering the syntax for XSD.exe!

Subversion and Windows Search

August 22nd, 2010 Rupert Benbrook No comments

I’m a regular Subversion user, specifically TortoiseSVN and VisualSVN, but I’m also a big fan of Windows Search, particularly with all the improvements introduced in Windows 7. However, when Windows Search indexes areas of the file system where you’ve checked out a Subversion repository to, then by default it will index the checked out files and the base files that Subversion keeps in the hidden .svn directories. In addition it indexes all the other files in the .svn directories. This means you can get a slightly odd search experience as you often get garbage in the results and two hits for every matching file, one that’s the file you’re looking for and one that’s the base file in the .svn directory.
Search results with duplicate entries
So, what’s the solution? Well one solution is to slightly alter the query you use when searching directory hierarchies that contain Subversion managed files. Just add -folder:\.svn to the end of the query.
Correct search results
It’s the correct set of results, but this isn’t ideal. What would be better is if Windows Search didn’t index the .svn directories in the first place. Unfortunately the user interface for managing Windows Search is quite basic and it’s not possible to add a rule that says exclude all directories named .svn anywhere they are encountered. You can only exclude specific .svn directories, and this isn’t practical.
I’m left wondering if there’s some way to achieve this through the Windows Search SDK? An interesting exercise for the future I think!

Migrated to WordPress

August 21st, 2010 Rupert Benbrook No comments

Well, I finally got fed up with Graffiti, it’s lack of features and pace of development, so I’ve switched to WordPress. So far, I’m quite impressed. The migration was a bit of a pain, but I got there in the end! Hopefully a new blog platform will re-energise my writing and I’ll get some more interesting posts up here.

Tags: ,

.NET Naming Conventions – VB.NET version

January 30th, 2010 Rupert Benbrook No comments

Not so long ago a colleague of mine, Josh Twist, did a post on .NET naming conventions using C# code to serve as an example. These are based on the .NET naming guidelines published on MSDN. Well, I happen to be doing a code review right now, but it’s in VB.NET. So I thought I’d do a VB.NET version and post it here for everyone’s reference. I’m sure that Eric Nelson will be very happy!

Option Explicit On
Option Strict On
Imports System
' Namespaces are PascalCased
Namespace Example.NamingConventions
    ' Class names are PascalCased
    Public Class ExampleClass
        ' All public fields, including constants are PascalCased
        Public Const PiAsAString As String = "3.14"
        ' All private fields are camelCased with an underscore prefix
        Private ReadOnly _privateMember As String
        ' All protected members are PascalCased
        Protected ProtectedField As Integer = 12
        ' All friend members are PascalCased
        Friend InternalField As Integer = 13
        ' All private methods are PascalCased
        ' *** NOTE - All parameters are camelCased
        Private Function Multiply(ByVal valueA As Double, ByVal valueB As Double) As Double
            ' local variables (scoped within a method) are camelCased (no underscore)
            Dim result As Double = valueA * valueB
            Return result
        End Function
        ' All private Properties are PascalCased
        ' *** NOTE - Acronyms of 2 characters are UPPERCASED (e.g. UI, IO)
        Private ReadOnly Property UIElementName() As String
            Get
                Throw New NotImplementedException()
            End Get
        End Property
        ' All (public and private) properties are PascalCased
        ' *** NOTE - Acronyms longer than 2 characters are PascalCased (e.g. Html, Xml)
        Public Property HtmlLength() As Integer
            Get
                Throw New NotImplementedException()
            End Get
            Set(ByVal value As Integer)
                Throw New NotImplementedException()
            End Set
        End Property
        ' All public methods are PascalCased
        ' *** NOTE - All parameters are camelCased
        ' *** NOTE - Abbreviations are not treated as Acronyms (so "Id" is Id, not ID).
        Public Sub AlignObjectById(ByVal id As String, ByVal alignment As Alignment)
            Throw New NotImplementedException()
        End Sub
        ' Nested classes are PascalCased, even private ones
        Private Class NestedClass
            Implements IDisposable
            Public Sub Dispose() Implements IDisposable.Dispose
                Throw New NotImplementedException()
            End Sub
        End Class
    End Class
    ' Enums are PascalCased and not plural (unless marked with <Flags> in which case the name should be plural)
    Public Enum Alignment
        ' Enum members are PascalCased
        Top
        Bottom
        Left
        Right
    End Enum
End Namespace
Tags: ,

BizTalk WCF Adapter Stack

November 25th, 2009 Rupert Benbrook No comments

For a while now I’ve been thinking about writing a post that documents all the gritty details of how the BizTalk messaging engine hooks into the WCF stack in the various WCF adapters. Now I don’t have to as Paolo Salvatori has done a stunning job:

http://blogs.msdn.com/paolos/archive/2009/11/17/customizing-and-extending-the-biztalk-wcf-adapters.aspx

Well done mate. This is by far the best explanation of how the BizTalk WCF adapter stack actually works. It should be in the documentation!

Tags: , ,

Visual Studio Theme Generator

November 17th, 2009 Rupert Benbrook No comments

I’ve just discovered this and it’s awesome. If you like tweaking your Visual Studio theme, but can’t be bothered with going through the huge number of different settings that Visual Studio presents you with, then this makes it miles easier:

http://frickinsweet.com/tools/Theme.mvc.aspx

Select a background, foreground and main colour, tweak the contrast, hit refresh to preview your selections, and then when you’re finished, click create to generate a vssettings file you can import directly into Visual Studio.

As the site says, frinkin’ sweet!

Check your INI file encoding

June 15th, 2009 Rupert Benbrook No comments

Last week I was assisting a customer with a production problem that appeared to be caused by the CLR JIT compiler inlining certain properties and methods. One of the quick workarounds we tried was to temporarily turn off JIT optimizations to see if it was indeed the inlining causing the problem. We did this using the .NET Framework Debugging Control INI file.

I got out Visual Studio, created a new text file, called it the same name as our EXE, but with an INI extension, and added the following to the file:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

Then I saved the file and copied it to the same directory as our EXE, and restarted the application. The strange thing was that it had no effect whatsoever! Still our problem was there, and we were convinced by this point that it was the JIT inlining. I even went to the lengths of putting together a quick sample application that reproduced our problem and still this INI file wasn’t working. After over an hour banging our heads on our collective desks I started getting paranoid about the precise text in the INI file, and that’s when it dawned on me. The default encoding of text files in Visual Studio is UTF-8 with a signature. A quick check of the advanced save options confirmed this:

Flipping this over to Windows codepage 1252 and resaving the file solved the problem. No more JIT optimizations and our problem disappears, finally confirming our suspicions. So it would seem that GetPrivateProfileString doesn’t like Unicode byte order mark signatures!

Tags: , ,

Unity and the Factory Method Pattern

April 23rd, 2009 Rupert Benbrook 2 comments

I’ve been playing around with Unity recently, and in particular I’ve been trying to get Unity to resolve dependencies from factory methods, and more specifically manage the lifetime of those dependencies. If you’re not familiar with the factory method pattern then check out Wikipedia.

Out of the box, Unity does support the ability to register a type that is created by a factory method using the StaticFactoryExtension class. This extension allows you to specify a delegate that creates the type, and that delegate can then call a static factory method, or even resolve another type from the container and call it’s factory method. I’m not going to cover the specifics of how to use the StaticFactoryExtension here, but pop on over to David Hayden’s blog for an example.

However, there’s a big drawback with the StaticFactoryExtension. It doesn’t allow you to specify any LifetimeManager, nor any InjectionMembers. This means that every time the factory method type is needed by a dependency, the factory method delegate is called. So you’re either forced to make your factory method manage object lifetimes, which isn’t nice, or put up with always having a new instance for every dependency. The lack of InjectionMembers also means that the factory method has to provide precisely the correctly configured object. This isn’t much of an issue for most factory methods, but it does provide more flexibility.

So, what’s the solution? Well, I was pretty sure that it was going to require me to write some kind of Unity extension, and whilst I’m comfortable with using Unity, I’d not tried writing an extension before, other than a LifetimeManager. Unfortunately for me this requires you to know a bit about how ObjectBuilder works, and that’s not a particularly well documented bit of Unity. So, armed with the Unity source I started digging. What follows is the result.

One of the first things I realized is that all I really need to do is modify the way that Unity builds objects at the stage where a specific constructor is called, and instead call a delegate to get the object instance. Everything else should stay the same. After a bit of digging I came across the UnityDefaultStrategiesExtension class, and in it’s Initialize method are the lines:

Context.BuildPlanStrategies.AddNew<DynamicMethodConstructorStrategy>(
    UnityBuildStage.Creation);
Context.BuildPlanStrategies.AddNew<DynamicMethodPropertySetterStrategy>(
    UnityBuildStage.Initialization);
Context.BuildPlanStrategies.AddNew<DynamicMethodCallStrategy>(
    UnityBuildStage.Initialization);

The bit that caught my eye was the DynamicMethodConstructorStrategy class. Looking in more detail at this class confirmed that it was responsible for generating part of a dynamic method that would actually call a constructor. This was the class I had to intercept and modify its behaviour. With this knowledge I started building my new extension, using the StaticFactoryExtension as an example.

First I defined how I wanted to configure the extension in an interface. This interface has the same method signatures that you see on IUnityContainer for the generic versions of RegisterType, just naming them RegisterFactory and excluding the ones that take a TTo type parameter. You could quite easily add the non-generic versions too, but I didn’t bother.

public delegate object FactoryMethodDelegate(IUnityContainer container);
public interface IFactoryMethodConfiguration : IUnityContainerExtensionConfigurator
{
    IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                   params InjectionMember[] injectionMembers);
    IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                   string name,
                                                   params InjectionMember[] injectionMembers);
    IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                   LifetimeManager lifetimeManager,
                                                   params InjectionMember[] injectionMembers);
    IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                   string name,
                                                   LifetimeManager lifetimeManager,
                                                   params InjectionMember[] injectionMembers);
}

Next, I defined the extension class by deriving from UnityContainerExtension and implementing the configuration interface.

public class FactoryMethodExtension : UnityContainerExtension, IFactoryMethodConfiguration
{
    public IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                          params InjectionMember[] injectionMembers)
    {
        return RegisterFactory<T>(factoryMethodDelegate, null, null, injectionMembers);
    }
    public IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                          string name,
                                                          params InjectionMember[] injectionMembers)
    {
        return RegisterFactory<T>(factoryMethodDelegate, name, null, injectionMembers);
    }
    public IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                          LifetimeManager lifetimeManager,
                                                          params InjectionMember[] injectionMembers)
    {
        return RegisterFactory<T>(factoryMethodDelegate, null, lifetimeManager, injectionMembers);
    }
    public IFactoryMethodConfiguration RegisterFactory<T>(FactoryMethodDelegate factoryMethodDelegate,
                                                          string name,
                                                          LifetimeManager lifetimeManager,
                                                          params InjectionMember[] injectionMembers)
    {
        Container.RegisterType<T>(name, lifetimeManager, injectionMembers);
        Context.Policies.Set<IFactoryMethodPolicy>(new FactoryMethodPolicy(() => factoryMethodDelegate(Container)),
                                                   NamedTypeBuildKey.Make<T>(name));
        return this;
    }
    protected override void Initialize()
    {
        Context.BuildPlanStrategies.Clear();
        Context.BuildPlanStrategies.AddNew<FactoryMethodConstructorStrategy>(UnityBuildStage.Creation);
        Context.BuildPlanStrategies.AddNew<DynamicMethodPropertySetterStrategy>(UnityBuildStage.Initialization);
        Context.BuildPlanStrategies.AddNew<DynamicMethodCallStrategy>(UnityBuildStage.Initialization);
    }
}

The two important pieces here are the Initialize method and the last RegisterFactory method. In Initialize I’m getting rid of the default BuildPlanStrategies and replacing them for my own. Actually, the only difference between these and the defaults defined in UnityDefaultStrategiesExtension is the FactoryMethodConstructorStrategy used in the creation stage, but I’ll come to that later.

In the last RegisterFactory method I register the from type as normal with the container, passing the LifetimeManager and InjectionMembers, but I also set an additional policy, IFactoryMethodPolicy, on the context. This wraps up the factory method delegate with the container into a more simple delegate that just returns an object, and is keyed using the same key that identifies the from type. This policy is used later when I need to call the factory delegate as part of building up the object. The FactoryMethodPolicy class is quite simple and just wraps the simplified delegate.

public delegate object FactoryMethodPolicyDelegate();
public interface IFactoryMethodPolicy : IBuilderPolicy
{
    FactoryMethodPolicyDelegate FactoryMethodPolicyDelegate { get; }
}
public class FactoryMethodPolicy : IFactoryMethodPolicy
{
    private readonly FactoryMethodPolicyDelegate factoryMethodPolicyDelegate;
    public FactoryMethodPolicy(FactoryMethodPolicyDelegate factoryMethodPolicyDelegate)
    {
        this.factoryMethodPolicyDelegate = factoryMethodPolicyDelegate;
    }
    public FactoryMethodPolicyDelegate FactoryMethodPolicyDelegate
    {
        get { return factoryMethodPolicyDelegate; }
    }
}

The final link in the chain is the FactoryMethodConstructorStrategy class.

public class FactoryMethodConstructorStrategy : DynamicMethodConstructorStrategy
{
    private static readonly MethodInfo FactoryMethodPolicyDelegateInvoke =
        typeof(FactoryMethodPolicyDelegate).GetMethod("Invoke");
    private static readonly MethodInfo GetFactoryMethodPolicyDelegateMethodInfo =
        typeof(FactoryMethodConstructorStrategy).GetMethod("GetFactoryMethodPolicyDelegate");
    public override void PreBuildUp(IBuilderContext context)
    {
        Guard.ArgumentNotNull(context, "context");
        if (GetFactoryMethodPolicyDelegate(context) == null)
        {
            base.PreBuildUp(context);
            return;
        }
        var buildContext = (DynamicBuildPlanGenerationContext)context.Existing;
        Label existingObjectNotNull = buildContext.IL.DefineLabel();
        buildContext.EmitLoadExisting();
        buildContext.IL.Emit(OpCodes.Brtrue, existingObjectNotNull);
        buildContext.EmitLoadContext();
        buildContext.IL.EmitCall(OpCodes.Call, GetFactoryMethodPolicyDelegateMethodInfo, null);
        buildContext.IL.EmitCall(OpCodes.Callvirt, FactoryMethodPolicyDelegateInvoke, null);
        buildContext.EmitStoreExisting();
        buildContext.IL.MarkLabel(existingObjectNotNull);
    }
    public static FactoryMethodPolicyDelegate GetFactoryMethodPolicyDelegate(IBuilderContext context)
    {
        var factoryMethodPolicy = context.Policies.Get<IFactoryMethodPolicy>(context.BuildKey);
        return factoryMethodPolicy == null ? null : factoryMethodPolicy.FactoryMethodPolicyDelegate;
    }
}

This class derives from the original DynamicMethodConstructorStrategy class whose behaviour I want to modify. I override the PreBuildUp method and check to see if there is an IFactoryMethodPolicy with a delegate defined for the build key in the context. If there isn’t I simply call the base implementation of PreBuildUp that will generate the dynamic method bits to call a constructor. However, if there is a delegate defined then I generate the dynamic method bits that call this delegate.

And once it’s all put together, that’s it! For good measure I’ve included the tests I used below. I’m certainly finding this useful in my projects, and if you do too I’d really like to hear about your experiences. Please feel free to comment on this post, even if it’s a bug you find!

 [TestFixture]
 public class FactoryMethodExtensionTests
 {
     public class TestStringFactory
     {
         private readonly string factory;
         public TestStringFactory(string factory)
         {
             this.factory = factory;
         }
         public string GetString()
         {
             return factory;
         }
     }
     public class TestStringDependency
     {
         private readonly string dependency;
         public TestStringDependency(string dependency)
         {
             this.dependency = dependency;
         }
         public string Dependency
         {
             get { return dependency; }
         }
     }
     [Test]
     public void FactoryMethodCanBeUsedToResolveTypeDependency()
     {
         var container = new UnityContainer();
         container.AddNewExtension<FactoryMethodExtension>();
         container.Configure<IFactoryMethodConfiguration>()
             .RegisterFactory<string>(c => "FactoryString");
         container.RegisterType<TestStringDependency>();
         var resolve = container.Resolve<TestStringDependency>();
         Assert.AreEqual("FactoryString", resolve.Dependency);
     }
     [Test]
     public void FactoryMethodCanUseContainerToResolveTypeToGetFactoryMethodUsedInDependency()
     {
         var container = new UnityContainer();
         container.AddNewExtension<FactoryMethodExtension>();
         container.RegisterInstance(new TestStringFactory("FactoryString"));
         container.Configure<IFactoryMethodConfiguration>()
             .RegisterFactory<string>(c => c.Resolve<TestStringFactory>().GetString());
         container.RegisterType<TestStringDependency>();
         var resolve = container.Resolve<TestStringDependency>();
         Assert.AreEqual("FactoryString", resolve.Dependency);
     }
     [Test]
     public void FactoryMethodExtensionLoadedStillResolvesNormally()
     {
         var container = new UnityContainer();
         container.AddNewExtension<FactoryMethodExtension>();
         container.RegisterType<object>();
         var resolve = container.Resolve<object>();
         Assert.NotNull(resolve);
     }
     [Test]
     public void FactoryMethodExtensionWithContainerLifetimeResolvesSameInstances()
     {
         var container = new UnityContainer();
         container.AddNewExtension<FactoryMethodExtension>();
         container.Configure<IFactoryMethodConfiguration>()
             .RegisterFactory<object>(c => new object(), new ContainerControlledLifetimeManager());
         var first = container.Resolve<object>();
         var second = container.Resolve<object>();
         Assert.AreSame(first, second);
     }
     [Test]
     public void FactoryMethodExtensionWithDefaultLifetimeResolvesDifferentInstances()
     {
         var container = new UnityContainer();
         container.AddNewExtension<FactoryMethodExtension>();
         container.Configure<IFactoryMethodConfiguration>()
             .RegisterFactory<object>(c => new object());
         var first = container.Resolve<object>();
         var second = container.Resolve<object>();
         Assert.AreNotSame(first, second);
     }
     [Test]
     public void FactoryMethodExtensionWithTransientLifetimeResolvesDifferentInstances()
     {
         var container = new UnityContainer();
         container.AddNewExtension<FactoryMethodExtension>();
         container.Configure<IFactoryMethodConfiguration>()
             .RegisterFactory<object>(c => new object(), new TransientLifetimeManager());
         var first = container.Resolve<object>();
         var second = container.Resolve<object>();
         Assert.AreNotSame(first, second);
     }
}
Tags: ,

Feature Jealousy

March 25th, 2009 Rupert Benbrook No comments

What’s got LINQ, but doesn’t have a yield keyword and no Action or multiline lambdas? What’s supposed to be an easy to learn language, but supports exception filters when other supposedly more complex languages don’t?

The answer, VB.NET.

“Co-evolution” has been announced for VB.NET 10.0 and C# 4.0. However, VB.NET doesn’t appear to be getting yield, and C# won’t be getting XML literals (which I personally think is a good thing).

I think VB.NET shed the “Beginners” bit from BASIC a long time ago. What do you think?

Oh, and whilst I’m at it, who’s up for an __il keyword in C# so you can have inline blocks of IL?

Tags: , ,

Be afraid?

March 25th, 2009 Rupert Benbrook No comments

According to the BBC this morning, the UK government is proposing to monitor social networking sites like Facebook, Bebo and MySpace. This comes the day after a report called for the government to scrap a quarter of all their databases as they are illegal and should be redesigned. Included in this list of databases is the Communications Database which plans to centralise details of all phone calls made and websites visited, and is open to 510 public authorities. Just imagine what you could find out about people with a bit of clever data mining across these databases. What about joining up other sources of information on the internet? What sort of information would you look up about yourself, friends or family? How would that differ from the sort of information that government departments or security services would want to look up? People are always curious about what other people are interested in.

Tags: