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

Automapper 4.2.1 LINQ projections work only with static Mapper.CreateMap?

我正在尝试在 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();

原因是以下块:

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 class 的实例,而您正在传递 IMapperConfiguration (接口)的实例。你认为它会产生同样的效果,但它并没有。 IMapperConfiguration 接口没有实现 IConfigurationProvider 接口,而 IConfigurationProviderProjectTo 接受的正确重载。但是,还有另一个 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。每当您有许多重载并且其中之一接受通用对象并且做 完全 与所有其他重载不同的事情时 - 这就是自找麻烦。