Search code examples
.netdependency-injectioninversion-of-controlioc-containersimple-injector

Simplejector Equivalent of StructureMap's Container.With().EqualTo()


With Simple Injector, is there an equivalent of StructureMap's Container.With("CustomerId").EqualTo(100).GetInstance<Customer>() (which looks up the property CustomerId via reflection)?


Solution

  • There is no equivalent to this in Simple Injector. Reason is that such a construct would typically lead to the Service Locator anti-pattern and would lead to a brittle configuration caused by the use of magic strings.

    The general (container independent) advice is therefore to use an abstract factory (as Mark Seemann explains here). The application can take a dependency on the abstract factory instead of using the container. You move the responsibility to the factory. Still you would need to create that dependency inside the factory. This example shows a solution:

    // Part of Composition Root
    private class SomeObjectFactory : ISomeObjectFactory
    {
        private readonly Container container;
    
        public SomeObjectFactory(Container container)
        {
            this.container = container;
        }
    
        public SomeObject Create(int someValue)
        {
            return new SomeObject(someValue,
                this.container.GetInstance<IOtherDependency>()
            );
        }
    }
    

    Although this works, the Create method will have to be changed every time the constructor of SomeObject changes.

    In general my advice is therefore to avoid mixing primitive values (such as int and string) with service dependencies in the same constructor when your goal is to do auto-wiring (where the container injects all dependencies for you). Although some containers have much more support build in for registering and resolving types that contain primitive values, it always leads to a DI configuration that needs more maintenance and is more brittle.

    Instead, try to either:

    1. Wrap the primitive value in a class that can be injected. i.e. inject an IConnectionManager instead of a string connectionString.
    2. Promote the primitive value to a property. With Simple Injector, this allows you to do explicit property injection (by registering an initialization delegate using RegisterInitializer), which enables compile-time verifiable wiring.

    The only exception to this rule would be when you have some sort of convention over configuration approach that enables you to automatically detect some sort of primitive values as shown here. Still however, I think you would have to take a good look at your design. When the same primitive (configuration) value is injected into multiple services, you are probably missing an abstraction. In that case, apply #1.

    This advice however, especially holds for configuration values. You are dealing with a runtime value. Using properties however, is still usable when dealing with runtime values:

    // Part of Composition Root
    private class SomeObjectFactory : ISomeObjectFactory
    {
        private readonly Container container;
    
        public SomeObjectFactory(Container container)
        {
            this.container = container;
        }
    
        public SomeObject Create(int someValue)
        {
            var instance = this.container.GetInstance<SomeObject>();
            instance.SomeValue = someValue;
            return instance;
        }
    }
    

    By promoting SomeValue to a property we allow the container to use auto-wiring on SomeObject and enable compile-time support on the injection of the runtime value of someValue.

    The use of such a factory however might seem a bit verbose and others might rather inject a Func<T> delegate:

    container.RegisterSingle<Func<int, SomeObject>>(someValue =>
    {
        var instance = this.container.GetInstance<SomeObject>();
        instance.SomeValue = someValue;
        return instance;
    });
    

    In this case you application logic can depend on a Func<int, SomeObject> delegate that can be injected into constructors. This removes a lot of seremony, with the downside of losing some expresiveness in the design of the application, since Func<int, SomeObject> is much less expresive as SomeObject Create(int someValue) is.

    In your particular case however, you seem to be resolving entities. This might not be the best thing to do, as Mark Seemann explained here. Besides, letting the container create entities that you feed with a runtime id seems odd to me, since an entity with an Id would typically be retrieved from the database.

    You are probably applying Domain-Driven Design and that's why you need those service dependencies on your entities. Instead of doing constructor or property injection, think about using method injection. Simply add the dependencies an entity's method need as method argument. You can then pass those dependencies in when calling that method from your command handler. The command handler can in that case still use plain old constructor injection. When I first saw this (I believe it was a presentation from Jimmy Nilsson) it felt it conflicted with everything I knew. However, after some time I started to see that this was actually a quite nice solution (for this particular scenario).