How to have SimpleInjector resolve viewmodel dependencies?

824 views Asked by At

I'm trying to use SimpleInjector 2.7.3 (IoC container) within an Asp.Net MVC + Web API application.

I've had a couple of problems trying to set it up for both MVC and Web API on the same project until I found this link:

http://methoddev.com/blg/let-s-talk-software/310/simple-injector-in-asp-net-mvc-webapi

After following the link's example, here's what I got:

One of my Web API controllers:

public class UserController : BaseApiController
{
    private readonly IUserService service;

    public UserController(IUserService userService)
    {
        // I should point that IUserService is being injected correctly here
        this.service = userService;
    }

    public IHttpActionResult Post(CreateUserRequest request)
    {
        return Ok();
    }
}

The problem happens when I try to execute the Post operation. The CreateUserRequest class itself has a dependency.

public class CreateUserRequest : IValidatableObject
{
    private readonly IValidator<CreateUserRequest> validator;

    public CreateUserRequest(IValidator<CreateUserRequest> _validator)
    {
        // _validator is not being injected, I'm getting null here
        validator = _validator;
    }

    public string SomeProperty { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // My validation logic here must call the validator injected
        // when the object was created.
        return null;
    }
}

I should point that IValidator is an interface from the FluentValidator package.

Anyway, when CreateUserRequest is instantiated the validator is null, which means it's not being injected.

When I'm creating the SimpleInjector Container I can see the type correctly registered, so I don't think that is a problem.

I did the following change to CreateUserRequest class:

public class CreateUserRequest : IValidatableObject
{
    private readonly CreateUserRequestValidator validator;

    // Changed here to the concrete class
    public CreateUserRequest(CreateUserRequestValidator _validator)
    {
        validator = _validator;
    }

    // ...
}

So, I changed the interface to a concrete class and I'm still receiving a null there.

The only thing I can imagine is that this is somehow related to the custom dependency resolver suggested by the aforementioned link. I needed to use that in order to have the same dependency resolution logic for both MVC and Web API. Here's the code:

public class SimpleInjectorDependencyResolver : System.Web.Mvc.IDependencyResolver,
  System.Web.Http.Dependencies.IDependencyResolver,
  System.Web.Http.Dependencies.IDependencyScope
{
    public SimpleInjectorDependencyResolver(Container container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        this.Container = container;
    }

    public Container Container { get; private set; }

    public object GetService(Type serviceType)
    {
        if (!serviceType.IsAbstract && typeof(IController).IsAssignableFrom(serviceType))
        {
            return this.Container.GetInstance(serviceType);
        }

        return ((IServiceProvider)this.Container).GetService(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.Container.GetAllInstances(serviceType);
    }

    IDependencyScope System.Web.Http.Dependencies.IDependencyResolver.BeginScope()
    {
        return this;
    }

    object IDependencyScope.GetService(Type serviceType)
    {
        return ((IServiceProvider)this.Container).GetService(serviceType);
    }

    IEnumerable<object> IDependencyScope.GetServices(Type serviceType)
    {
        return this.Container.GetAllInstances(serviceType);
    }

    void IDisposable.Dispose()
    {
    }
}

I don't really know a lot of the plumbing behind MVC and Web API (specially the custom dependency resolver feature), so, I'm really stuck on this one.

I appreciate any help figuring that out. Thanks.

--UPDATE--

In addition to the answer given by Steven, I would like to leave a link to whoever falls into the same problem. It's a great resource:

https://brettedotnet.wordpress.com/2014/07/16/web-api-and-interface-parameters/

1

There are 1 answers

1
Steven On BEST ANSWER

The reason why your view model object isn't auto-wired by Simple Injector is because both MVC and Web API don't build view model objects using the IDependencyResolver. So creating a special dependency resolver won't work. If you want to let your view models to be auto-wired, you will have to override the default model binder in MVC and Web API.

But I urge you not to do this. In my opinion, a model binder should just do data conversion and a view model should be a plain DTO. Although it is fine to mark view models with validation attributes, letting them have behavior using services that might even trigger any database communication is a big no-no in my book. This can complicate development tremendously.

This however means that this validator should be injected elsewhere. Without making any changes to your architecture, this basically means you will have to inject that validator in the controller instead:

public class UserController : BaseApiController
{
    private readonly IUserService service;
    private readonly IValidator<CreateUserRequest> validator;

    public UserController(IUserService userService,
        IValidator<CreateUserRequest> validator)
    {
        this.service = userService;
        this.validator = validator;
    }
}

Obviously this can easily complicate your controllers with extra dependencies and logic, but that's because validation is a cross-cutting concern that you would like to probably keep out of your controllers.

If you try to address this, you will eventually end up with a message passing architecture such as described here.