Подтвердить что ты не робот

Сопоставление Automapper с интерфейсом: исключение "TypeLoadException"

Я работаю над MVC-приложением, которое использует Inversion of Control и, следовательно, широко использует типы интерфейсов, при этом конкретные реализации вводятся зависимым преобразователем по мере необходимости. Интерфейсы сущностей наследуются от базового интерфейса, который описывает некоторые основные функции управления для сущностей. ViewModels также широко используются.

Приложение использует Automapper, и я создал сопоставления из моделей представлений на различные интерфейсы сущностей. Конфигурация отображения корректно проверяется. Однако, когда я вызываю Automapper для выполнения сопоставления, код выходит из строя с помощью TypeLoadException.

Я считаю, что Automapper способен отображать на интерфейсы (см. this от Jimmy Bogard).

Кажется возможным, что генератор прокси-сервера Automapper опустил добавление MyMethod() в прокси-сервер, и это вызывает исключение, когда Reflection пытается создать тип.

Если это не так, как мне заставить эту карту работать? Я пропустил что-то очевидное?

Здесь представлено упрощенное консольное приложение, демонстрирующее сценарий, и которое воспроизводит ошибку при запуске:

public interface IEntity
{
    string Foo { get; set; }
    string Bar { get; set; }
    string MyMethod();
}

public class MyEntity : IEntity
{
    public string Foo { get; set; }
    public string Bar { get; set; }
    public string MyMethod()
    {
        throw new NotImplementedException();
    }
}

public class MyViewModel
{
    public string Foo { get; set; }
    public string Bar { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        AutomapperConfig();
        MyViewModel vm = new MyViewModel { Foo = "Hello", Bar = "World" };
        IEntity e = Mapper.Map<MyViewModel, IEntity>(vm);
        Console.WriteLine(string.Format("{0} {1}", e.Foo, e.Bar));
    }

    private static void AutomapperConfig()
    {
        Mapper.Initialize(cfg => {
            cfg.CreateMap<MyViewModel, IEntity>();
        });
        Mapper.AssertConfigurationIsValid();
    }
}

Исключение составляет:

InnerException: System.TypeLoadException
   HResult=-2146233054
   Message=Method 'MyMethod' in type 'Proxy<AutomapperException.IEntity_AutomapperException_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null>' from assembly 'AutoMapper.Proxies, Version=0.0.0.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005' does not have an implementation.
   Source=mscorlib
   TypeName=Proxy<AutomapperException.IEntity_AutomapperException_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null>
   StackTrace:
        at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
        at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
        at System.Reflection.Emit.TypeBuilder.CreateType()
        at AutoMapper.Impl.ProxyGenerator.CreateProxyType(Type interfaceType)
        at AutoMapper.Impl.ProxyGenerator.GetProxyType(Type interfaceType)
        at AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.CreateObject(ResolutionContext context)
        at AutoMapper.Mappers.TypeMapObjectMapperRegistry.NewObjectPropertyMapMappingStrategy.GetMappedObject(ResolutionContext context, IMappingEngineRunner mapper)
        at AutoMapper.Mappers.TypeMapObjectMapperRegistry.PropertyMapMappingStrategy.Map(ResolutionContext context, IMappingEngineRunner mapper)
        at AutoMapper.Mappers.TypeMapMapper.Map(ResolutionContext context, IMappingEngineRunner mapper)
        at AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context)
4b9b3361

Ответ 1

При использовании интерфейса в качестве адресата, AutoMapper создаст для вас тип прокси, но это поддерживает только свойства.

Чтобы обойти эту проблему, вы можете указать AutoMapper, как объект назначения должен быть сконструирован с использованием ConstructUsing при вашем сопоставлении, поэтому в вашем примере выше ваша карта создания будет выглядеть так:

cfg.CreateMap<MyViewModel, IEntity>().ConstructUsing((ResolutionContext rc) => new MyEntity());

Для справки я нашел это из этой статьи SO: fooobar.com/info/691819/...