How do I use AutoMapper with Ninject.Web.Mvc?

2019-03-18 16:58发布

问题:

Setup

I have an AutoMapperConfiguration static class that sets up the AutoMapper mappings:

static class AutoMapperConfiguration()
{
    internal static void SetupMappings()
    {
        Mapper.CreateMap<long, Category>.ConvertUsing<IdToEntityConverter<Category>>();
    }
}

where IdToEntityConverter<T> is a custom ITypeConverter that looks like this:

class IdToEntityConverter<T> : ITypeConverter<long, T> where T : Entity
{
    private readonly IRepository _repo;

    public IdToEntityConverter(IRepository repo)
    {
        _repo = repo;
    }

    public T Convert(ResolutionContext context)
    {
        return _repo.GetSingle<T>(context.SourceValue);
    }
}

IdToEntityConverter takes an IRepository in its constructor in order to convert an ID back to the actual entity by hitting up the database. Notice how it doesn't have a default constructor.

In my ASP.NET's Global.asax, this is what I have for OnApplicationStarted() and CreateKernel():

protected override void OnApplicationStarted()
{
    // stuff that's required by MVC
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);

    // our setup stuff
    AutoMapperConfiguration.SetupMappings();
}

protected override IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Bind<IRepository>().To<NHibRepository>();

    return kernel;
}

So OnApplicationCreated() will call AutoMapperConfiguration.SetupMappings() to set up the mappings and CreateKernel() will bind an instance of NHibRepository to the IRepository interface.

Problem

Whenever I run this code and try to get AutoMapper to convert a category ID back to a category entity, I get an AutoMapperMappingException that says no default constructor exists on IdToEntityConverter.

Attempts

  1. Added a default constructor to IdToEntityConverter. Now I get a NullReferenceException, which indicates to me that the injection isn't working.

  2. Made the private _repo field into a public property and added the [Inject] attribute. Still getting NullReferenceException.

  3. Added the [Inject] attribute on the constructor that takes an IRepository. Still getting NullReferenceException.

  4. Thinking that perhaps Ninject can't intercept the AutoMapperConfiguration.SetupMappings() call in OnApplicationStarted(), I moved it to something that I know is injecting correctly, one of my controllers, like so:

    public class RepositoryController : Controller
    {
        static RepositoryController()
        {
            AutoMapperConfiguration.SetupMappings();
        }
    }
    

    Still getting NullReferenceException.

Question

My question is, how do I get Ninject to inject an IRepository into IdToEntityConverter?

回答1:

You have to give AutoMapper access to the DI container. We use StructureMap, but I guess the below should work with any DI.

We use this (in one of our Bootstrapper tasks)...

    private IContainer _container; //Structuremap container

    Mapper.Initialize(map =>
    {
        map.ConstructServicesUsing(_container.GetInstance);
        map.AddProfile<MyMapperProfile>();
    }


回答2:

@ozczecho's answer is spot-on, but I'm posting the Ninject version of the code because it has one little caveat that caught us for a while:

IKernel kernel = null; // Make sure your kernel is initialized here

Mapper.Initialize(map =>
{
    map.ConstructServicesUsing(t => kernel.Get(t));
});

You can't just pass in kernel.Get to map.ConstructServicesUsing because that method has a params parameter in addition to the Type. But since params are optional, you can just create the lambda expression to generate an anonymous function to get you what you need.