Search code examples
c#unity-containerioc-containerunity2.0

Custom object factory extension for Unity


I am using the Unity IoC container, and I need to intercept any calls to Resolve for a certain base interface, and run my own custom code to construct those types.

In other words, in the sample code below, when I call container.Resolve<IFooN>(), if it hasn't got an instance of the concrete implementing type, it calls MyFactoryFunction to construct one, otherwise I want it to return the cached copy.

The standard Unity container is not able to construct these objects (update: because they are .NET remoting objects, so the concrete classes do not exist in any assembly on the local computer), and I don't want to create them up front and store them with RegisterInstance.

interface IFoo : IBase { ... }
interface IFoo2 : IBase { ... }

...
container.Resolve<IFoo2>();

...
IBase MyFactoryFunction(Type t)
{
    ...
}

I'm assuming I can create a Unity extension to do this, but I was wondering if there is already a solution out there I can borrow.


Solution

  • For completeness, I should add another answer that works under Unity 2, since my other answer no longer works. It is slightly more involved since you need to make a custom builder policy. Thanks to ctavares from the Unity project who provided lots of help on this thread in implementing this:

    public class FactoryUnityExtension : UnityContainerExtension
    {
        private ICustomFactory factory;
        private CustomFactoryBuildStrategy strategy;
    
        public FactoryUnityExtension(ICustomFactory factory)
        {
            this.factory = factory;
        }
    
        protected override void Initialize()
        {
            this.strategy = new CustomFactoryBuildStrategy(factory, Context);
            Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
            Context.Policies.Set<ParentMarkerPolicy>(new ParentMarkerPolicy(Context.Lifetime), new NamedTypeBuildKey<ParentMarkerPolicy>());
        }
    }
    
    public class ParentMarkerPolicy : IBuilderPolicy
    {
        private ILifetimeContainer lifetime;
    
        public ParentMarkerPolicy(ILifetimeContainer lifetime)
        {
            this.lifetime = lifetime;
        }
    
        public void AddToLifetime(object o)
        {
            lifetime.Add(o);
        }
    }
    
    public interface ICustomFactory
    {
        object Create(Type t);
        bool CanCreate(Type t);
    }
    
    public class CustomFactoryBuildStrategy : BuilderStrategy
    {
        private ExtensionContext baseContext;
        private ICustomFactory factory;
    
    
        public CustomFactoryBuildStrategy(ICustomFactory factory, ExtensionContext baseContext)
        {
            this.factory = factory;
            this.baseContext = baseContext;
        }
    
        public override void PreBuildUp(IBuilderContext context)
        {
            var key = (NamedTypeBuildKey)context.OriginalBuildKey;
    
            if (factory.CanCreate(key.Type) && context.Existing == null)
            {
                context.Existing = factory.Create(key.Type);
                var ltm = new ContainerControlledLifetimeManager();
                ltm.SetValue(context.Existing);
    
                // Find the container to add this to
                IPolicyList parentPolicies;
                var parentMarker = context.Policies.Get<ParentMarkerPolicy>(new NamedTypeBuildKey<ParentMarkerPolicy>(), out parentPolicies);
    
                // TODO: add error check - if policy is missing, extension is misconfigured
    
                // Add lifetime manager to container
                parentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
                // And add to LifetimeContainer so it gets disposed
                parentMarker.AddToLifetime(ltm);
    
                // Short circuit the rest of the chain, object's already created
                context.BuildComplete = true;
            }
        }
    }