Skip to content

Resolution constructor and property injection

Alex Maris edited this page Jan 26, 2016 · 3 revisions

Resolution API

    // Basic named and unnamed resolution
    var instance = container.Resolve<MyConcreteType>(); // Unnamed
    instance = container.Resolve<MyConcreteType>("Name"); // Named

    // By default we try and resolve every concrete type,
    // whether they are registered or not, but we don't "fallback"
    // to an unnamed registration if resolving a named one.
    //
    // We can easily change that behaviour though.
    container.Resolve<MyConcreteType>(
        new ResolveOptions() {UnregisteredResolutionAction = UnregisteredResolutionActions.Fail});
    container.Resolve<MyConcreteType>("Name", 
        new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution });

    // We can pass parameters using name/value pairs.
    // By default the "greediest" constructor is used if possible,
    // but we will fall all the way back to a default constructor if
    // necessary to construct the type.
    container.Resolve<MyConcreteType>(new NamedParameterOverloads() { { "property1", 12 }, { "property2", "Testing" } });

    // We also have the same overloads on CanResolve
    // to determine if resolution is possible
    container.CanResolve<MyConcreteType>(
        new ResolveOptions() { UnregisteredResolutionAction = UnregisteredResolutionActions.Fail });
    container.CanResolve<MyConcreteType>("Name",
        new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution });
    container.CanResolve<MyConcreteType>(new NamedParameterOverloads() { { "property1", 12 }, { "property2", "Testing" } });

    // And also for TryResolve, which will resolve if possible
    // and return a boolean to indicate whether resolution completed
    var resolved = container.TryResolve<MyConcreteType>("MyName", out resolvedType); // resolved is true if resolution was possible

    // Resolve all resolves all registrations for a type, named or otherwise
    // as IEnumerable<ResolveType>
    container.Resolve<IMyInterface, MyType1>("MyType1"); // named registration
    container.Resolve<IMyInterface, MyType2>("MyType2"); // 2nd named registration
    IEnumerable<IMyInterface> myResolvedTypes = container.ResolveAll<IMyInterface>(); // Returns both registrations

Constructor Injection

    // By default we will use the "greediest" constructor
    // if we can..
    public Constructors(ITestInterface1 dependency, string value1, int value2)
    {
    }

    // .. but we will fallback if we can't - rather than
    // throwing a resolution exception.
    public Constructors(ITestInterface1 dependency)
    {
    }

    // For types that are expensive to construct and may not
    // always be needed we provide automatic lazy factories
    // by putting Func<T> in the constructor
    public Constructors(Func<MyExpensiveObject> expensiveObjectFactory)
    {
        var instance = expensiveObjectFactory.Invoke();
    }

    // We also allow lazy factories that specify a named registration
    // to resolve using Func<String, T>:
    public Constructors(Func<String, MyExpensiveObject> expensiveObjectFactory)
    {
        var instance = expensiveObjectFactory.Invoke("MyNamedRegistration");
    }

    // And we also allow you to specify named parameter overloads to lazy factories
    // using Func<String, IDictionary<string, object>, T>:
    public Constructors(Func<string, IDictionary<String, object>, TestClassWithParameters> factory)
    {
        _Factory = factory;
        Prop1 = _Factory.Invoke("", new Dictionary<String, object> { { "stringProperty", "Testing" }, { "intProperty", 22 } });
    }

Property Injection

    // If constructor injection isn't possible/preferable we can also
    // use property injection.
    //
    // Any public read/write property that we can resolve, that isn't 
    // already set to a value, is resolved by the container:
    class TestClassPropertyDependencies
    {
        public ITestInterface Property1 { get; set; } // Will be set if we can resolve and isn't already set
        public ITestInterface2 Property2 { get; set; } // Will be set if we can resolve and isn't already set
        public int Property3 { get; set; } // Will be ignored
        public string Property4 { get; set; } // Will be ignored

        public TestClassDefaultCtor ConcreteProperty { get; set; } // Will be set if we can resolve and isn't already set

        public ITestInterface ReadOnlyProperty { get; private set; } // Will be ignored
        public ITestInterface2 WriteOnlyProperty { internal get; set; } // Will be ignored (no way to know if it's already set)
    }

    // -- SNIP -- //

    var input = new TestClassPropertyDependencies();
    container.BuildUp(input); // Properties are now set

Child / Nested Containers

    // If we need to control scope or lifetime of particular
    // registrations we can use child containers.
    //
    // We can get a child container from any container instance
    var child = container.GetChildContainer();

    // A child container will "bubble up" resolve requests if it can't resolve
    // a type itself
    container.Register<ITestInterface, TestClassDefaultCtor>();
    var result = child.Resolve<ITestInterface>();  // Will resolve with the parent container

    // If a type is registered with the child container it will resolve that
    // instead of the same type registered with the parent:
    container.Register<ITestInterface>(parentInstance);
    child.Register<ITestInterface>(childInstance);
    child.Resolve<ITestInterface>(); // Will return "childInstance"

    // We can let the child container go out of scope because the 
    // parent holds no reference to it. Or we can Dipose() it which 
    // will DISPOSE ALL INSTANCES/SINGLETONS REGISTERED WITH IT if they
    // support IDisposable:
    child.Dispose(); // Disposes container and all disposable instances/singletons