阻止AutoMapper将字符串视为LINQ ProjectTo中的集合
本文关键字:ProjectTo 集合 LINQ AutoMapper 字符串 阻止 | 更新日期: 2023-09-27 17:59:10
我有以下一组类要映射(仅在一个方向上,从Data*
到Api*
):
// Top level
public class DataEntity
{
public NestedDataEntity Nested { get; set; }
// ... other primitive/complex properties
}
public class ApiEntity
{
public NestedApiEntity Nested { get; set; }
// ... other primitive/complex properties
}
// Nested level
public class NestedDataEntity
{
public string Items { get; set; }
}
public class NestedApiEntity
{
public IEnumerable<ApiSubItem> Items { get; set; }
}
public class ApiSubItem
{
// there are properties here. Not needed for the sake of example
}
映射在Profile
中配置,如下代码位所示:
// mapping profile
public class MyCustomProfile : Profile
{
public MyCustomProfile()
{
CreateMap<DataEntity, ApiEntity>();
CreateMap<NestedDataEntity, NestedApiEntity>();
CreateMap<string, IEnumerable<ApiSubItem>>()
.ConvertUsing<TextToSubItemsConverter>();
}
}
// type converter definition
public class TextToSubItemsConverter :
ITypeConverter<string, IEnumerable<ApiSubItem>>
{
public IEnumerable<ApiSubItem> Convert(
string dataItems, IEnumerable<ApiSubItem> apiItems, ResolutionContext context)
{
// actually, deserialize & return an ApiSubItem[]
// here just return some fixed array
return new ApiSubItem[]
{
new ApiSubItem(),
new ApiSubItem(),
new ApiSubItem(),
};
}
}
// Main
public class Program
{
public static void Main(string[] args)
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<MyCustomProfile>();
});
Mapper.AssertConfigurationIsValid();
DataEntity dataEntity = new DataEntity()
{
Nested = new NestedDataEntity()
{
Items = "Ignored text",
},
};
// This maps ok, no issues
ApiEntity apiEntity = Mapper.Map<DataEntity, ApiEntity>(dataEntity);
IQueryable<DataEntity> dataEntities = new[] { dataEntity }.AsQueryable();
// This exposes the System.Char to ApiSubItem issue
ApiEntity apiEntityProjected = dataEntities.ProjectTo<ApiEntity>().First();
}
}
映射配置在启动时通过了初始验证,但当需要实际映射时,我会得到异常:
System.InvalidOperationException:从System.Char到my.whatever.namespace.ApiSubItem.缺少映射。使用Mapper.CreateMap.创建
如果我完全省略了string
和IEnumerable<ApiSubItem>
之间的配置,则初始验证会抱怨相同的上下文:
上下文:从类型System.Char到my.whatever.namespace.ApiSubItem 的映射
尽管添加时,它似乎没有被AutoMapper拾取。
映射发生在静态上下文中,通过DbSet<DataEntity>
上的LINQ查询之上的ProjectTo<ApiEntity>()
调用。我已经检查过转换器不需要任何依赖项,以防万一。
AutoMapper是5.0.2,运行在ASP.NetCore1RTM下的MVC API web应用程序中。
我在SO上检查过类似的问题,但没有运气。有人知道如何让这种场景发挥作用吗?我想我不是第一个尝试(自动)从string
映射到集合的人。TA
EDIT在示例中添加了失败和非失败情况
EDIT 2经过进一步搜索,有人建议忽略该字段并在AfterMap()
中执行映射。使用此配置文件,不会引发异常,但生成的Items
字段为空:
CreateMap<DataEntity, ApiEntity>();
CreateMap<NestedDataEntity, NestedApiEntity>()
.ForMember(api => api.Items, options => options.Ignore())
.AfterMap((data, api) =>
{
api.Items = Mapper.Map<IEnumerable<ApiSubItem>>(data.Items);
});
CreateMap<string, IEnumerable<ApiSubItem>>()
.ConvertUsing<TextToSubItemsConverter>();
编辑3改写问题标题,使其更具体地用于投影
我想实际的答案是沿着"投影和转换器/解析器不能很好地一起工作";。来自AutoMapper Queryable.扩展wiki:
并非所有映射选项都可以支持,因为生成的表达式必须由LINQ提供程序进行解释。[…]
不支持:
- […]
- 在映射之前/之后
- 自定义解析器
- 自定义类型转换器
编辑解决方法:
在这个特定的场景中,我后来发现拥有一个中间DTO实体是有用的,它可以获取更多的字段,而最终API实体不需要这些字段,但业务逻辑仍然需要这些字段。
在这样的DTO实体中,我像在DataEntity
中一样保留了string
字段,所以我可以使用投影,并让AutoMapper/LinqToSQL从DB中只获取所需的字段。
然后,在DTO实体和内存中已经存在的API实体之间,我可以应用第二个映射,该映射还利用了string ==> IEnumerable<ApiSubItem>
映射的自定义转换器。HTH