Automapper 4.2.1 LINQ投影仅适用于静态Mapper.CreateMap?

时间:2016-04-12 07:19:07

标签: c# entity-framework entity-framework-6 automapper automapper-4

我试图在Entity Framework IQueryables上使用Automapper投影。

在应用程序启动时,我创建并添加了我的所有映射配置文件,这些配置文件使用非静态CreateMap方法创建映射。

所有这些配置文件都在我的IoC容器中注册。

虽然我在mappingConfiguration的实例中看到了映射配置文件,但我得到了缺少的映射异常。

可能是什么问题?我错过了什么吗?我使用的是Automapper 4.2.1

我注意到在添加静态Mapper.CreateMap时,它可以正常工作。预测只适用于静态API吗?我想避免使用静态API。

完整代码:

public class ItemEntityToItemView : Profile
{
    public override void Configure()
    {
        CreateMap<ItemEntity, ItemView>();

        // Without this line, I get missing Map type configuration.
        Mapper.CreateMap<ItemEntity, ItemView>();
    }
}


public interface IEntitiesProjector
{
    IQueryable<T> SelectTo<T>(IQueryable source);
}

public class EntitiesProjector : IEntitiesProjector
{
    private readonly IMapperConfiguration _mapperConfig;

    public EntitiesProject(IMapperConfiguration mapperConfig)
    {
        _mapperConfig = mapperConfig;
    }

    public IQueryable<T> SelectTo<T>(IQueryable source)
    {
        return source.ProjectTo<T>(_mapperConfig);
    }
}

public class ItemsRepository : IITemsRepository
{
    public IQueryable<ItemEntity> GetById(int id)
    {
        return _dbSet.Where(x => x.Id == id);
    }
}

public class Service
{
    private readonly IEntitiesProjector _projector;

    public Service(IEntitiesProject entitiesProjector)
    {
        _projector = entitiesProjector;
    }

    public List<T> GetItem(int id)
    {
        IQueryable<ItemEntity> itemsQueryable = ItemsRepository.GetById(id);

        return _projector.SelectTo<ItemView>(itemsQueryable);
    }
}

My Autofac registration :

builder.RegisterAssemblyTypes().AssignableTo(typeof(Profile)).As<Profile>();

builder.Register(c => new MapperConfiguration(cfg =>
{
    cfg.CreateMap<IdentityUser, AspNetUser>().ReverseMap();
})).AsSelf().As<IMapperConfiguration>().SingleInstance();

builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve)).As<IMapper>().InstancePerLifetimeScope();

builder.Register<EntitiesProjector>().As<IEntitiesProjector>().SingleInstance();

1 个答案:

答案 0 :(得分:10)

原因如下:

public class EntitiesProjector : IEntitiesProjector
{
    private readonly IMapperConfiguration _mapperConfig;

    public EntitiesProject(IMapperConfiguration mapperConfig)
    {
        _mapperConfig = mapperConfig;
    }

    public IQueryable<T> SelectTo<T>(IQueryable source)
    {
        return source.ProjectTo<T>(_mapperConfig);
    }
}

source.ProjectTo是一个有5次重载的扩展方法。在文档中,他们在那里传递MappingConfiguration类的实例,并且您正在传递IMapperConfiguration(接口)的实例。你认为它会产生同样的效果,但事实并非如此。 IMapperConfiguration接口未实现IConfigurationProvider接口,而IConfigurationProvider)是ProjectTo接受的正确重载。但是,ProjectTo还有另一个重载,它接受“object parameters”。因为它接受对象 - 它将匹配任何不适合其他重载的东西。所以你真正称之为ProjectTo(object)重载,这与配置无关,你的IMapperConfiguration以及配置文件和地图完全被忽略。

Quickfix将是

public class EntitiesProjector : IEntitiesProjector
{
    private readonly IConfigurationProvider _mapperConfig;

    public EntitiesProjector(IMapperConfiguration mapperConfig)
    {
        _mapperConfig = (IConfigurationProvider)mapperConfig;
    }

    public IQueryable<T> SelectTo<T>(IQueryable source)
    {
        return source.ProjectTo<T>(_mapperConfig);
    }
}

但是当然你最好在你的容器中将你的配置注册为IConfigurationProvider,这只是一个快速解决方案,以确保问题真的在这里。

至于静态Mapper.CreateMap - 它是静态的,所以无论你传递给ProjectTo的是什么都有效。

作为旁注,这将向您展示如何不设计api。每当你有很多重载并且其中一个接受通用对象并且完全与所有其他重载不同时 - 这就是要求麻烦。