Как использовать AutoMapper с Ninject.Web.Mvc?

Настройка

У меня есть статический класс AutoMapperConfiguration, который устанавливает сопоставления AutoMapper:

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

где IdToEntityConverter<T> - это пользовательский ITypeConverter, который выглядит следующим образом:

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 принимает IRepository в своем конструкторе, чтобы преобразовать идентификатор обратно в фактический объект, нажав на базу данных. Обратите внимание, что у него нет конструктора по умолчанию.

В моем ASP.NET Global.asax это то, что у меня есть для OnApplicationStarted() и CreateKernel():

protected override void OnApplicationStarted()
{
    // stuff that 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() вызовет AutoMapperConfiguration.SetupMappings(), чтобы настроить сопоставления, а CreateKernel() привяжет экземпляр NHibRepository к интерфейсу IRepository.

Проблема

Всякий раз, когда я запускаю этот код и пытаюсь получить AutoMapper для преобразования идентификатора категории обратно в объект категории, я получаю AutoMapperMappingException, который говорит, что конструктор по умолчанию не существует на IdToEntityConverter.

Попытки

  • Добавлен конструктор по умолчанию в IdToEntityConverter. Теперь я получаю NullReferenceException, что указывает на то, что инъекция не работает.

  • Создал частный _repo в публичное свойство и добавил атрибут [Inject]. Все еще получаю NullReferenceException.

  • Добавлен атрибут [Inject] в конструкторе, который принимает IRepository. Все еще получаю NullReferenceException.

  • Думая, что, возможно, Ninject не может перехватить вызов AutoMapperConfiguration.SetupMappings() в OnApplicationStarted(), я переместил его в то, что, как я знаю, правильно вводит, один из моих контроллеров, например:

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

    По-прежнему получать NullReferenceException.

Вопрос

Мой вопрос: как мне получить Ninject для ввода IRepository в IdToEntityConverter?

Ответ 1

Вы должны предоставить AutoMapper доступ к контейнеру DI. Мы используем StructureMap, но я думаю, что нижеследующее должно работать с любым DI.

Мы используем это (в одной из наших задач Bootstrapper)...

    private IContainer _container; //Structuremap container

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

Ответ 2

@ozczecho ответ на месте, но я отправляю версию кода Ninject, потому что у него есть одно небольшое предостережение, которое нас поймало какое-то время:

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

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

Вы не можете просто передать kernel.Get в map.ConstructServicesUsing, потому что этот метод имеет параметр params в дополнение к типу. Но поскольку параметры необязательны, вы можете просто создать выражение лямбда для создания анонимной функции, чтобы получить то, что вам нужно.