Kevin Smith
Kevin Smith

Reputation: 14466

Unity IoC InjectionFactory not respecting DependencyOverride

I'm currently trying to wrap a class in decorator and inject in one of the dependencies at runtime. I currently have an interface of IStorage that is implemented by a StorageCacheDecorator and a Storage. The StorageCacheDecorator takes in a IStorage and the Storage object takes in aContext` object. However the context object needs to be passed in every time these classes are resolved.

public interface IStorage
{

}

public class Storage : IStorage
{
    public Context Context { get; }

    public Storage(Context context)
    {
        this.Context = context;
    }
}

public class StorageCacheDecorator : IStorage
{
    public IStorage InnerStorage { get; }

    public StorageCacheDecorator(IStorage innerStorage)
    {
        this.InnerStorage = innerStorage;
    }
}

public class Context
{
}

I've omitted the implementation details and the test below gives an example of my problem

    [Test]
    public void ShouldResolveWithCorrectContext()
    {
        var context = new Context();

        var container = new UnityContainer();

        container.RegisterType<Storage>();

        container.RegisterType<IStorage>(
            new InjectionFactory(c => new StorageCacheDecorator(
                c.Resolve<Storage>())));

        var resolve = container.Resolve<IStorage>(new DependencyOverride<Context>(context));

        Assert.That(resolve, Is.TypeOf<StorageCacheDecorator>());

        var cacheDecorator = ((StorageCacheDecorator)resolve);
        Assert.That(cacheDecorator.InnerStorage, Is.TypeOf<Storage>());

        var storage = ((Storage)cacheDecorator.InnerStorage);
        Assert.That(storage.Context, Is.SameAs(context));
    }

However if we remove the decorator the test passes

    [Test]
    public void ShouldResolveWithCorrectContext1()
    {
        var context = new Context();

        var container = new UnityContainer();

        container.RegisterType<IStorage, Storage>();

        var resolve = container.Resolve<IStorage>(new DependencyOverride<Context>(context));

        Assert.That(resolve, Is.TypeOf<Storage>());

        Assert.That(((Storage)resolve).Context, Is.SameAs(context));
    }

How do I get the InjectionFactory to respect the DependencyOverride?

Upvotes: 2

Views: 1217

Answers (1)

quetzalcoatl
quetzalcoatl

Reputation: 33566

First of all, I'm pretty sure that what you (and today - me) have stumbled upon is a bug in Unity.

I managed to diagnose it a bit thanks to this excellent article from where I got an example of a BuilderStrategy. After I replaced my InjectionFactory with this extension, it worked for some cases, and didn't work for other.

After some investigation, it seems that all resolutions in Unity work against the same Container and its configuration, and any DependencyOverrides are dragged along the call in an IBuilderContext object, in a set of resolverOverrides which contains policies constructed from DependencyOverrides. The IBuilderContext provides a GetResolverOverride(Type) method which allows the implementation to get the value overrides for given Type.

So, if you explicitly ask the IBuilderContext.GetResolverOverride for an override for your Storage Context, you will get the Same-Context-Object you expect.

However, if you ever try to ask the Container itself, you will get a Context object resolved along the standard rules. Not that overridden-at-the-point-of-resolution.

This is why any attempt to container.Resolve(..) in the InjectionFactory delegate like here:

    container.RegisterType<IStorage>(
        new InjectionFactory(c => new StorageCacheDecorator(
            c.Resolve<Storage>())));  // <-- this C is Container

will fail to satisfy overrides, because.. Container has no idea about overrides!

It would have to be:

    container.RegisterType<IStorage>(
        new InjectionFactory(c => new StorageCacheDecorator(
            builderContext.Resolve<Storage>())));

which, if ever implemented, could access the GetResolverOverride and build a proper Storage with correct override. However, no such method exists, and, what's worse, at this point of code you don't have access to the IBuilderContext - InjectionFactory does not give it to you. Probably only extensions&friends can access that.

Fun fact: IBuilderContext has GetResolverOverride but doesn't have any sorts of .Resolve. So, if you'd take the code from that article and if you used the PreBuildUp as in that article to do your own resolution logic, you'd have to either use Container (and fail on resolver overrides), or get in a hell of inspecting everything and doing manually all sub-resolutions needed to construct the instance. IBuilderContext provides you a pretty-looking NewBuildUp() method, which seems a great shortcut, but .. it uses the base Container and doesn't forward resolver overrides.

Seeing how complicated and unintuitive it is, and how easy it is to accidentally drop that set of resolveroverrides and fallback to vanilla context, I'm pretty sure that InjectionFactory is EVIL/BUGGED/MISDESIGNED/etc: it gives you "c", your main Container instance, so you just have no way of resolving the parameters properly with respect to the overrides. Instead of that main container, we should get some sort of derived container, or extra builder object, etc..

Using the code at found in the article I was able to hack something that used this GetResolverOverride to initialize the new object instance in a way I wanted, but it was in no way generic and totally not reusable (I just called proper .GetResolverOverride to get the value and passed it right into new MyObject(value).. but, god, that's just awful. However, I needed as a part of a test setup, so I can live with that until the code gets refactored.

Now, let's get back to your case. Obviously you could do a similar thing, but it turns out that in case of decorators there is a much easier way: just get rid of the InjectionFactory. Your original instance initialization code was probably more complicated, but if by any chance it actually was as simple as in your example:

    container.RegisterType<IStorage>(
        new InjectionFactory(c =>
            new StorageCacheDecorator(   // <- NEW &
                 c.Resolve<Storage>()    // <- RESOLVE
        )));

you should have actually used declarative way instead of imperative buggy InjectionFactory:

    container.RegisterType<IStorage, StorageCacheDecorator>(
          new InjectionConstructor(
               new ResolvedParameter<Storage>()
          ));

the net effect is exactly the same: new object created, the single-argument constructor is called, and Storage is resolved and used as that argument. I tried it with your test cases and it worked just fine.

Instead of 'ResolvedParameter` you can also use direct object instances, like:

    container.RegisterType<IStorage, StorageCacheDecorator>(
          new InjectionConstructor(
               "foo", 5, new Shoe()
          ));

but, just as with new StorageDecorator in your original example, the InjectionConstructor needs to get all of the parameters for the constructor, and obviously we won't get a compile-time error when the constructor parameters change in the future. It's also much more limited than InjectionFactory as it needs to get all parameters specified up front.

All the more reason for for hating Unity..

Upvotes: 5

Related Questions