Questions about Migration from StructureMap

Aug 7, 2012 at 2:15 AM

First I would like to thank you for your help in regards to the service locator anti-patter.  I notice that my last question about migration was deleted.  I apologize in advance if you do not wish to answer questions in regards migrations in this forum.  If you can provide the proper channel to do so, I will gladly follow it next time.

 

Two questions:

In our old StructureMap registration, we are providing default parameters to constructors like below:

            For<IInterface>()
                .Use<InterfaceA>()
                .Ctor<string>("parameter").Is("hello");

 In Simple Injector, would this be the equivalent?

            container.Register<IInterface>(() =>
                {
                    const string parameter = "hello";
                    return new InterfaceA(parameter);
                });

 I tried looking (not very hard) in the documentation but I did not see it.

 

Secondly, we are using T4MVC in our project, and when I try to run the project, I get an error:

For the container to be able to create Controller, it should contain exactly one public constructor, but it has 2.
Parameter name: TConcrete

 I think T4MVC requires a default constructor with no parameters.  Please advise.  Thanks again.

Coordinator
Aug 7, 2012 at 7:09 AM
Edited Aug 9, 2012 at 10:00 AM

I decided to delete the question, since it was a duplicate from that on Stackoverflow. In general I find this forum more suited for questions about bugs, feature requests, and discussions that aren't a good fit for Stackoverflow. Stackoverflow is a nice channel because it gives you, as a information seeker, the possibility to get feedback from other DI experts in the field than just the few people that are actively monitoring this forum. I am actively monitoring both Stackoverflow and this forum, so I don't mind you asking here :-)

Out of the box, Simple Injector does not have a way to override primitive constructor arguments, as StructureMap does. There are multiple ways around this:

1. You can register a delegate that handels the creation of the type (just as you did in your example):

container.Register<IInterface>(() =>
    return new InterfaceA("hello", "bar"));

Downside of this approach is that since you create the type manually, the container will not do any auto-wiring for you, which means that you need to change this code any time you make changes to the constructor of that implementation. You will also miss out some other options, such as registering initializers directly on the implementation (since only the service is known to the container).

2. If you can modify the type, you can move the primitive arguments out of the constructor and into properties:

container.Register<IInterface, InterfaceA>();

container.RegisterInitializer<InterfaceA>(a =>
{
    a.Parameter = "hello";
    a.SomeValue = "bar";
});

This allows you to have the implementation type be fully recognized by the container, which allows it to do auto-wiring. Downside is that you need to change the type (if possible) and that it might be unintuitive to move those arguments out when they are required (since required dependencies should normally be placed in the constructor).

3. You can also change the way the constructor does auto-wiring, which allows you add the possibility to register those primitive constructorr arguments. I personally wouldn't go this way as first option, since I believe that when your application design is good, you won't need this option often. Still it is an option. Take a look at this blog post about Primitive Dependencies with the Simple Injector. On top of the extensions described in this article you can add this code. With this all in place, you can write the following code:

var container = new Container();

var convention = new WithParameterConvention(container);

container.Options.RegisterParameterConvention(convention);

container.Register<IInterface, InterfaceA>(
    convention.WithParameter("parameter", "hello"),
    convention.WithParameter("someValue", "bar"));

These are basically your options.

For your T4MVC Controller class, there are again multiple options. You can change the constructor resolution behavior to allow the container to pick a constructor when it contains multiple, but it is much easier to register the Controller with a delegate, just as you did with your InterfaceA.

I hope this helps.

Marked as answer by dot_NET_Junkie on 11/4/2013 at 1:59 AM
Aug 8, 2012 at 10:02 PM

Thank you again for your quick response.  I will keep this in mind and post future questions in StackOverflow.  But since I am following up on the previous question, I will do so here.

When you say "register the Controller with a delegate", do you mean explicitly registering the controller with parameters for its dependencies?  I would imagine this would work b/c the default behavior of

container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

 would be overridden?  At the same time, whenever we would create a new controller, we would have to register it manually, correct?

 

One last question, in StructureMap, we are forcing the it to look for interfaces in assemblies as follows:

            ObjectFactory.Configure(x =>
            {
                x.Scan(scan =>
                {
                    scan.Assembly("XYZ.Web");
                    scan.Assembly("XYZ.Data");
                    scan.Assembly("XYZ.Services");
                });
            });

I would assume we would have to do that manually also in SimpleInjector, correct?

 

Thanks in advance again for your help.

Coordinator
Aug 9, 2012 at 5:45 AM
Edited Aug 10, 2012 at 5:19 AM

All your services should have a single public constructor. I must admit that I'm not familiar with T4MVC, but if T4MVC is generating Controllers for you with multiple constructors, change the T4 template, to have those controllers have a single unambiguous definition of what dependencies they require.

If however you need to register types that are out of your control, you need to do things differently. The typical advice is to register the service with a delegate, for instance:

container.Register<SomeController>(() => new SomeController(
    container.GetInstance<SomeDependency>());

However, since you are batch registering controllers using the RegisterMvcControllers extension method, you already registered that controller. There are several options. You could for instance enable overriding in the container:

container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

container.Options.AllowOverridingRegistrations = true;

container.Register<SomeController>(() => new SomeController(
    container.GetInstance<SomeDependency>());

container.Options.AllowOverridingRegistrations = false;

The RegisterMvcControllers simply queries the assemblies metadata and gets the controller types from the assembly. Instead of using the RegisterMvcControllers method, you can do this by hand and filter out the types you want to hand-register:

using SimpleInjector.Extensions;

var controllerTypes =
    from type in Assembly.GetExecutingAssembly().GetExportedTypes()
    where type.Name.EndsWith("Controller", StringComparison.Ordinal)
    where typeof(IController).IsAssignableFrom(type)
    where !type.IsAbstract
    select type;

// Filter out controllers that should be registered manually.
controllerTypes = controllerTypes.Where(t => t != typeof(SomeController));

controllerTypes.ToList().ForEach(t => container.Register(t));

container.Register<SomeController>(() => new SomeController(
    container.GetInstance<SomeDependency>());

A third option is to ditch the RegisterMvcControllers method completely. It is not strictly needed, since the Simple Injector will be able to resolve unregistered concrete types with a single public constructor anyway (and will do so using the transient lifetime, which is required for controllers). This means you can just register that single controller with multiple constructor explicitly, and you're done. Downside is however, that a call to Verify() won't check the correctness of your controllers, since those controllers aren't registered (yet). You should always try to verify the container's configuration. In that case you should manually verify this, for instance by replacing the Verify method with the following:

var controllerTypes =
    from type in Assembly.GetExecutingAssembly().GetExportedTypes()
    where type.Name.EndsWith("Controller", StringComparison.Ordinal)
    where typeof(IController).IsAssignableFrom(type)
    where !type.IsAbstract
    select type;

// Instead of verify:
foreach (var type in controllerTypes)
{
    container.GetInstance(type);
}

As you've already seen the use of LINQ queries over .NET's type system. This is normally the way of doing batch registration in Simple Injector (except when dealing with generic types, since registering generic types can get pretty complicated). Although many DI frameworks contain an advanced API for doing convention based registration, we found that doing this with custom LINQ queries is often easier to write, easier to understand, and even more flexible than using such an API. That's why there is no API for doing this.

I'm not sure what StructureMap's scan.Assembly("...") exactly does, but I can imagine it to be something like this in Simple Injector:

using SimpleInjector.Extensions;

var serviceAssembly = typeof(XYZ.Services.SomeType).Assembly;

var registrations =
    from type in serviceAssembly.GetExportedTypes()
    where type.GetInterfaces().Length > 0
    select type;

foreach (var type in registrations)
{
    container.Register(type.GetInterfaces().First(), type);
}

Although this query is for one specific assembly, you can easily refactor it to a method, so that it can work with any assembly it is provided. Always try to keep your Composition Root as maintainably as possible.

I hope this helps.


Aug 9, 2012 at 8:43 PM

Thank you again for your help.  We will have to re-evaluate since having to register each controller would be a little too tedious for our application.  In any case, hopefully the information you provided can become part a "migration from" guide for Simple Injector.  Thanks again!

Coordinator
Aug 10, 2012 at 5:10 AM
Edited Aug 10, 2012 at 5:17 AM

Another option is to override the constructor resolution behavior of the container. You can change this behavior for all registered types, but I wouldn't advice this, since you should strive to have a single public constructor for your services. However, you can easily change this behavior just for Controller types by implementing a custom IConstructorResolutionBehavior class:

public class ControllerConstructorResolutionBehavior
    : IConstructorResolutionBehavior
{
    private IConstructorResolutionBehavior defaultBehavior;

    public ControllerConstructorResolutionBehavior(
        IConstructorResolutionBehavior defaultBehavior)
    {
        this.defaultBehavior = defaultBehavior;
    }

    [DebuggerStepThrough]
    public ConstructorInfo GetConstructor(Type serviceType,
        Type implementationType)
    {
        var constructorWithMostParameters = (
            from constructor in implementationType.GetConstructors()
            orderby constructor.GetParameters().Length descending
            select constructor)
            .FirstOrDefault();

        if (!typeof(IController).IsAssignableFrom(implementationType) ||
            constructorWithMostParameters == null)
        {
            return this.defaultBehavior
                .GetConstructor(serviceType, implementationType);
        }

        return constructorWithMostParameters;
    }
}

Registration of this class is done as follows:

var container = new Container();

container.Options.ConstructorResolutionBehavior =
    new ControllerConstructorResolutionBehavior(
        container.Options.ConstructorResolutionBehavior);

With this in place you can simply batch register your controllers as you did before:

container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

You can find other examples of custom constructor overload resolutions here and here.