How do I use the Decorator Pattern with Unity without explicitly specifying every parameter in the InjectionConstructor
Asked Answered
M

7

39

This helpful article from David Haydn (EDIT: scam link removed, it could have been this article) shows how you can use the InjectionConstructor class to help you set up a chain using the decorator pattern with Unity. However, if the items in your decorator chain have other parameters in their constructor, the InjectionConstructor must explicitly declare each one of them (or Unity will complain that it can't find the right constructor). This means that you can't simply add new constructor parameters to items in the decorator chain without also updating your Unity configuration code.

Here's some example code to explain what I mean. The ProductRepository class is wrapped first by CachingProductRepository and then by LoggingProductRepostiory. Both CachingProductRepository and LoggingProductRepository, in addition to taking a IProductRepository in their constructor, also need other interfaces from the container.

    public class Product 
    {
        public int Id;
        public string Name;
    }

    public interface IDatabaseConnection { }

    public interface ICacheProvider 
    { 
        object GetFromCache(string key);
        void AddToCache(string key, object value);
    }

    public interface ILogger
    {
        void Log(string message, params object[] args);
    }


    public interface IProductRepository
    {
        Product GetById(int id);    
    }

    class ProductRepository : IProductRepository
    {
        public ProductRepository(IDatabaseConnection db)
        {
        }

        public Product GetById(int id)
        {
            return new Product() { Id = id, Name = "Foo " + id.ToString() };
        }
    }

    class CachingProductRepository : IProductRepository
    {
        IProductRepository repository;
        ICacheProvider cacheProvider;
        public CachingProductRepository(IProductRepository repository, ICacheProvider cp)
        {
            this.repository = repository;
            this.cacheProvider = cp;
        }

        public Product GetById(int id)
        {       
            string key = "Product " + id.ToString();
            Product p = (Product)cacheProvider.GetFromCache(key);
            if (p == null)
            {
                p = repository.GetById(id);
                cacheProvider.AddToCache(key, p);
            }
            return p;
        }
    }

    class LoggingProductRepository : IProductRepository
    {
        private IProductRepository repository;
        private ILogger logger;

        public LoggingProductRepository(IProductRepository repository, ILogger logger)
        {
            this.repository = repository;
            this.logger = logger;
        }

        public Product GetById(int id)
        {
            logger.Log("Requesting product {0}", id);
            return repository.GetById(id);
        }
    }

Here's a (passing) unit test. See the comments for the bits of surplus configuration I want to remove the need for:

    [Test]
    public void ResolveWithDecorators()
    {
        UnityContainer c = new UnityContainer();            
        c.RegisterInstance<IDatabaseConnection>(new Mock<IDatabaseConnection>().Object);
        c.RegisterInstance<ILogger>(new Mock<ILogger>().Object);
        c.RegisterInstance<ICacheProvider>(new Mock<ICacheProvider>().Object);

        c.RegisterType<IProductRepository, ProductRepository>("ProductRepository");

        // don't want to have to update this line every time the CachingProductRepository constructor gets another parameter
        var dependOnProductRepository = new InjectionConstructor(new ResolvedParameter<IProductRepository>("ProductRepository"), new ResolvedParameter<ICacheProvider>());
        c.RegisterType<IProductRepository, CachingProductRepository>("CachingProductRepository", dependOnProductRepository);

        // don't want to have to update this line every time the LoggingProductRepository constructor changes
        var dependOnCachingProductRepository = new InjectionConstructor(new ResolvedParameter<IProductRepository>("CachingProductRepository"), new ResolvedParameter<ILogger>());
        c.RegisterType<IProductRepository, LoggingProductRepository>(dependOnCachingProductRepository);
        Assert.IsInstanceOf<LoggingProductRepository>(c.Resolve<IProductRepository>());
    }
Mikiso answered 24/5, 2011 at 11:25 Comment(6)
well, i never used unity before and maybe this is way off, but can't you use injectionfactory instead of injectionconstructor?Brom
@Brom an interesting suggestion. The Action would presumably have to call the concrete constructor though, meaning that the Unity configuration code would still need to be called whenever the construtor changed. It would at least result in a compile error though, alerting you to the need for change.Mikiso
@DarkSquirrel42, on second thoughts, if you just use a single InjectionFactory whose Func builds the whole chain, using the container to fulfil other dependencies, although you are still not isolated from constructor parameter changes, is at least much less confusing than my original code, and requires only one registration in the container as well.Mikiso
link to article is brokenFraze
that "helpful article" is now a scam site, might want to fix that.Galinagalindo
Does no-one who pointed out the broken link have edit rights? Just fix it.Iodous
M
29

Another approach, thanks to a suggestion from @DarkSquirrel42, is to use an InjectionFactory. The downside is that the code still needs updating every time a new constructor parameter is added to something in the chain. The advantages are much easier to understand code, and only a single registration into the container.

Func<IUnityContainer,object> createChain = container =>
    new LoggingProductRepository(
        new CachingProductRepository(
            container.Resolve<ProductRepository>(), 
            container.Resolve<ICacheProvider>()), 
        container.Resolve<ILogger>());

c.RegisterType<IProductRepository>(new InjectionFactory(createChain));
Assert.IsInstanceOf<LoggingProductRepository>(c.Resolve<IProductRepository>());
Mikiso answered 24/5, 2011 at 13:0 Comment(1)
This also has the advantage that changes to the constructors will generate compile time instead of runtime errors.Einstein
A
13

See this article on implementing a decorator container extension. This should get you to where you want to be with regards to not needing to modify your configuration if your constructor signatures change.

Animus answered 28/7, 2011 at 17:1 Comment(1)
This is my favourite solution!Sidran
P
6

Another solution involves adding type parameters to your code base in order to help Unity resolving your decorated types. Luckily Unity is perfectly capable of resolving type parameters and their dependencies on its own, so we don't have to care about constructor parameters when defining the decorator chain.

The registration would look as follows:

unityContainer.RegisterType<IService, Logged<Profiled<Service>>>();

Here is a basic example implementation. Note the templated decorators Logged<TService> and Profiled<TService>. Look below for some drawbacks I've noticed so far.

public interface IService { void Do(); }

public class Service : IService { public void Do() { } }

public class Logged<TService> : IService where TService : IService
{
    private TService decoratee;
    private ILogger logger;

    public Logged(ILogger logger, TService decoratee) {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    public void Do() {
        logger.Debug("Do()");
        decoratee.Do();
    }
}

public class Profiled<TService> : IService where TService : IService
{
    private TService decoratee;
    private IProfiler profiler;

    public Profiled(IProfiler profiler, TService decoratee) {
        this.decoratee = decoratee;
        this.profiler = profiler;
    }

    public void Do() {
        profiler.Start();
        decoratee.Do();
        profiler.Stop();
    }
}

Drawbacks

  • A faulty registration like uC.RegisterType<IService, Logged<IService>>(); will result in an infinite recursion that stack-overflows your application. This can be a vulnerability in a plug-in architecture.
  • It uglyfies your code base to some degree. If you ever give up Unity and switch to a different DI framework those template parameters will make no sense to anyone anymore.
Poseidon answered 11/9, 2013 at 18:14 Comment(0)
S
5

I knocked up a fairly crude extension method for this, which behaved as expected when I ran it:

public static class UnityExtensions
{
    public static IUnityContainer Decorate<TInterface, TDecorator>(this IUnityContainer container, params InjectionMember[] injectionMembers)
        where TDecorator : class, TInterface
    {
        return Decorate<TInterface, TDecorator>(container, null, injectionMembers);
    }

    public static IUnityContainer Decorate<TInterface, TDecorator>(this IUnityContainer container, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers)
        where TDecorator : class, TInterface
    {
        string uniqueId = Guid.NewGuid().ToString();
        var existingRegistration = container.Registrations.LastOrDefault(r => r.RegisteredType == typeof(TInterface));
        if(existingRegistration == null)
        {
            throw new ArgumentException("No existing registration found for the type " + typeof(TInterface));
        }
        var existing = existingRegistration.MappedToType;

        //1. Create a wrapper. This is the actual resolution that will be used
        if (lifetimeManager != null)
        {
            container.RegisterType<TInterface, TDecorator>(uniqueId, lifetimeManager, injectionMembers);
        }
        else
        {
            container.RegisterType<TInterface, TDecorator>(uniqueId, injectionMembers);
        }

        //2. Unity comes here to resolve TInterface
        container.RegisterType<TInterface, TDecorator>(new InjectionFactory((c, t, sName) =>
        {
            //3. We get the decorated class instance TBase
            var baseObj = container.Resolve(existing);

            //4. We reference the wrapper TDecorator injecting TBase as TInterface to prevent stack overflow
            return c.Resolve<TDecorator>(uniqueId, new DependencyOverride<TInterface>(baseObj));
        }));

        return container;
    }
}

And in your setup:

container.RegisterType<IProductRepository, ProductRepository>();

// Wrap ProductRepository with CachingProductRepository,
// injecting ProductRepository into CachingProductRepository for
// IProductRepository
container.Decorate<IProductRepository, CachingProductRepository>();

// Wrap CachingProductRepository with LoggingProductRepository,
// injecting CachingProductRepository into LoggingProductRepository for
// IProductRepository
container.Decorate<IProductRepository, LoggingProductRepository>();
Spoliation answered 21/4, 2017 at 10:20 Comment(6)
Could you explain how this deals with recursion? I cannot understand container.Registrations.First(r => r.RegisteredType == typeof(TInterface)).Mapped to type...Flashback
The line you reference will look at the existing registrations, get the existing registration for the interface (the concrete to be decorated - existing) and keep a note of that concrete type; the decorator is then registered, replacing existing. When it comes to resolution the decorator will use that concrete type (existing) and inject it into the decorator, but all other usages of your interface will use the decorator. This way it is possible to decorate many times if needed. It might need to be adapted if you use named registrations.Spoliation
I understand that but cannot catch why you always retrieve first from the list of registrations. What in the case you have more than one registered type with the same interface?Flashback
The only way you could have two registered types for one interface is if you used a named registration to differentiate between the two (hence the First() - it assumes it will just get one it - maybe Single would be better, idk...), otherwise you have a conflict there (how would Unity know which to use in a given case?). If you are using named registrations this code would need adapting.Spoliation
This is quite nice! Definite +1 from me, but I kind of wonder why do you say it's "crude"? The code looks nice and does exactly what it should. About the only thing I'd change / add is make a check when looking for the existing type - if it hasn't been registered yet throw an exception with the appropriate message... and when looking for the type make sure it hasn't been registered with a name (albeit this is debatable).Standifer
I agree, I've modified the post to check existing registrations. I've also changed this code slightly since I posted this in light of a bug where this code was always decorating the first matching registration every time, hence existing is now the last registration with a matching interface rather than the first registration.Spoliation
D
3

The most succinct answer which works great is mentioned in another stackoverflow post by Mark Seeman. It is concise, and does not require me to use named registrations or suggest that I use Unity extensions. Consider an interface called ILogger with two implementations namely Log4NetLogger and a decorator implementation called DecoratorLogger. You can register the DecoratorLogger against the ILogger interface as follows:

container.RegisterType<ILogger, DecoratorLogger>(
    new InjectionConstructor(
        new ResolvedParameter<Log4NetLogger>()));
Decompose answered 1/5, 2017 at 7:28 Comment(0)
M
0

While I was waiting for answers on this, I came up with a rather hacky workaround. I've created an extension method on IUnityContainer that lets me register a decorator chain using reflection to create the InjectionConstructor parameters:

static class DecoratorUnityExtensions
{
    public static void RegisterDecoratorChain<T>(this IUnityContainer container, Type[] decoratorChain)
    {
        Type parent = null;
        string parentName = null;
        foreach (Type t in decoratorChain)
        {
            string namedInstance = Guid.NewGuid().ToString();
            if (parent == null)
            {
                // top level, just do an ordinary register type                    
                container.RegisterType(typeof(T), t, namedInstance);
            }
            else
            {
                // could be cleverer here. Just take first constructor
                var constructor = t.GetConstructors()[0];
                var resolvedParameters = new List<ResolvedParameter>();
                foreach (var constructorParam in constructor.GetParameters())
                {
                    if (constructorParam.ParameterType == typeof(T))
                    {
                        resolvedParameters.Add(new ResolvedParameter<T>(parentName));
                    }
                    else
                    {
                        resolvedParameters.Add(new ResolvedParameter(constructorParam.ParameterType));
                    }
                }
                if (t == decoratorChain.Last())
                {
                    // not a named instance
                    container.RegisterType(typeof(T), t, new InjectionConstructor(resolvedParameters.ToArray()));
                }
                else
                {
                    container.RegisterType(typeof(T), t, namedInstance, new InjectionConstructor(resolvedParameters.ToArray()));
                }
            }
            parent = t;
            parentName = namedInstance;
        }
    }
}

This allows me to configure my container with a much more readable syntax:

[Test]
public void ResolveWithDecorators2()
{
    UnityContainer c = new UnityContainer();
    c.RegisterInstance<IDatabaseConnection>(new Mock<IDatabaseConnection>().Object);
    c.RegisterInstance<ILogger>(new Mock<ILogger>().Object);
    c.RegisterInstance<ICacheProvider>(new Mock<ICacheProvider>().Object);

    c.RegisterDecoratorChain<IProductRepository>(new Type[] { typeof(ProductRepository), typeof(CachingProductRepository), typeof(LoggingProductRepository) });

    Assert.IsInstanceOf<LoggingProductRepository>(c.Resolve<IProductRepository>());

}

I'd still be interested to know if there is a more elegant solution to this with Unity

Mikiso answered 24/5, 2011 at 11:51 Comment(0)
M
0

I know that this post is a little bit outdated, but in fact there is no fully functional Unity decorator implementation for the latest releases (there are a lots of breaking changes, see Unity wiki).

I've took @garryp answer (which is, in my opinion, the only correct answer here) and modified it according to the latest Unity container API changes:

public static IContainerRegistry RegisterDecorator<TInterface, TDecorator>(this IContainerRegistry container, ITypeLifetimeManager lifetimeManager, Type[] additionalInterfaces, params InjectionMember[] injectionMembers)
    where TDecorator : class, TInterface
{    
    var unityContainer = container.GetContainer();

    var existingRegistration = unityContainer.Registrations.LastOrDefault(r => r.RegisteredType == typeof(TInterface));

    if (existingRegistration == null)
    {
        throw new ArgumentException("No existing registration found for the type " + typeof(TInterface));
    }

    var existing = existingRegistration.MappedToType;
    var uniqueId = Guid.NewGuid().ToString();

    // 1. Create a wrapper. This is the actual resolution that will be used
    if (lifetimeManager != null)
    {
        unityContainer.RegisterType<TDecorator>(uniqueId, lifetimeManager, injectionMembers);
    }
    else
    {
        unityContainer.RegisterType<TDecorator>(uniqueId, injectionMembers);
    }

    unityContainer.RegisterType<TInterface, TDecorator>();

    if (additionalInterfaces != null)
    {
        foreach (var additionalInterface in additionalInterfaces)
        {
            unityContainer.RegisterType(additionalInterface, typeof(TDecorator));
        }
    }

    unityContainer.RegisterFactory<TDecorator>(DecoratorFactory);

    return container;

    object DecoratorFactory(IUnityContainer c)
    {
        // 3. We get the decorated class instance TBase
        var baseObj = c.Resolve(existing);

        // 4. We reference the wrapper TDecorator injecting TBase as TInterface to prevent stack overflow
        return c.Resolve<TDecorator>(uniqueId, new DependencyOverride<TInterface>(baseObj));
    }
}

The differences are:

  • IContainerRegistry type is being used instead of IUnityContainer - that's because I use PRISM wrappers over Unity container
  • additionalInterfaces optional parameter is added in order to be able to register decorators which also implements other interfaces
  • Logic is modified so that it fits the current Unity API implementation
Marilumarilyn answered 31/8, 2020 at 6:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.