使用 Source 中的 NotMapped/Computed 属性映射到目标时的 Automapper ProjectTo<>() 问题

Posted

技术标签:

【中文标题】使用 Source 中的 NotMapped/Computed 属性映射到目标时的 Automapper ProjectTo<>() 问题【英文标题】:Automapper ProjectTo<>() issue when using NotMapped/Computed property in Source to map in destination 【发布时间】:2017-10-01 17:08:09 【问题描述】:

我只有在使用 ProjectTo 时才收到错误,我无法理解根本问题。 (Automapper 版本使用 4.2.1.0) "在 LINQ to Entities 中不支持指定的类型成员 'Tags'。 仅支持初始化程序、实体成员和实体导航属性。” 我们也可以在 DTO 中进行这种操作,但我应该坚持只在实体方面进行操作。 请让我知道在不升级版本的情况下我可以处理此问题的方法或解决方法。 TIA 我希望实体的 Tags 属性的计算值需要映射到 DTO 的 Tags 属性,但是当我以正常方式进行时,这可以正常工作。

来源/目的地类型

public class Template : IEntity<int>
    
        public string Name  get; set; 

        public int Id  get; set; 

        public string _Tags  get; set; 

        [NotMapped]
        public List<string> Tags
        
            get  return _Tags == null ? null : JsonConvert.DeserializeObject<List<string>>(_Tags); 
            set  _Tags = JsonConvert.SerializeObject(value); 
               
    

实体配置

internal sealed class TemplateConfig : EntityTypeConfiguration<Template>
    
        public TemplateConfig()
        
            Ignore(x => x.Tags);
            HasKey(x => x.Id)
                .Map(m =>
                
                    m.ToTable("Template");
                    m.Property(x => x.Id).HasColumnName("ID");
                    m.Property(x => x.Name).HasColumnName("Name");
                    m.Property(x => x._Tags).HasColumnName("Tags");
                );
        
    

目标 DTO:

public class Template
    
        public int Id  get; set; 

        public string Name  get; set; 

        public List<string> Tags  get; set; 
    

映射配置

Mapper.CreateMap<Template, DTO.Template>();               

Mapper.CreateMap<DTO.Template, Template>(); 


//These are just for information, but getting error only when using ProjectTo. (Ignore about the OData thing)




 public virtual async Task<IQueryable<TDto>> Get(ODataQueryOptions<TDto> query)
            
                try
                
                    var expands = query.GetExpandedPropertyNames();
                    //Assume the collection has the data from db
                    var test = Collection.ToList();
                    //getting the exception here
                    return await Collection.ProjectTo<TDto>(null, expands).AsTask();
                
                catch(Exception ex)
                
                    throw ex;
                
            

    //Controller method
    public override async Task<IQueryable<Template>> Get(ODataQueryOptions<Template> query)
            
                try
                
                   List<Template> result = (await base.Get(query)).ToList();

                    return result.AsEnumerable().AsQueryable();
                
                catch(Exception ex)
                
                    throw ex;
                
            

【问题讨论】:

【参考方案1】:

这是因为 .ProjectTo&lt;&gt;(...) 正在 SQL 中为您构建 select 语句。当 .Tags 在您的对象之间映射时,它被包含在 select 语句中,然后实体框架抱怨它不能这样做(因为 NotMapped 属性)。

您可以使用普通的.ToList(),而不是使用ProjectTo&lt;&gt;(...),然后使用.Select(Mapper.Map&lt;TDto&gt;)Mapper.Map&lt;List&lt;TDto&gt;&gt;(list)

这应该可行,因为实体框架将从字符串字段填充您的标签字段,并且 automapper 可以完成映射。

public virtual async Task<IQueryable<TDto>> Get(ODataQueryOptions<TDto> query)      

    try
    
        var expands = query.GetExpandedPropertyNames();

        //Assume the collection has the data from db
        var test = Collection.ToList();

        var entities = expands.ToList();

        // you can either use .Select to project using LINQ
        var dtos = await entities.Select(Mapper.Map<TDto>).AsTask();

        // or you can use Mapper.Map to a list of entities.
        dtos = await Mapper.Map<List<TDto>>(entities).AsTask();

        return dtos;
    
    catch(Exception ex)
    
        // side note, don't throw ex, you'll lose the stack trace
        throw;
    

【讨论】:

是的,使用 Mapper.Map 可以工作,但我的问题只出在 ProjectTo 上。我不能取消这个实现,我只需要坚持 ProjectTo 可能只需要一些调整就可以了。 Automapper 根本不支持这种情况,还是在最近的版本中修复了它,或者我做错了什么? 除非将逻辑复制到地图中。您可以将 Map 设置为从 _Tags 属性中读取,然后在那里对其进行反序列化。 Automapper 无法自动为您执行此操作。 感谢大家的回答。我通过在使用 ProjectTo 之前添加 ToList 解决了这个问题,但不确定这是否是正确的解决方法。 (将其转换为列表,但会影响性能)我想自动映射器在处理计算值、Json 序列化或 NotMapped 属性时没有问题。这样的事情奏效了。 var test = Collection.ToList().AsQueryable(); return await test.ProjectTo&lt;TDto&gt;(null, expands).AsTask(); 是的,这基本上是我的原始答案。很高兴你成功了。 @Ixalln 是的。 :)【参考方案2】:

您是否尝试过在 MappingConfiguration 中实现忽略?我不能完全说出您遇到问题的方向,但类似:

Mapper.CreateMap<Template, DTO.Template>()
    .ForMember(dest => dest.Tags, opts => opts.Ignore());

【讨论】:

是的,我确实尝试过忽略,但在这种情况下,我没有在标签中获取数据。它只是一个空列表。只有当我使用 ProjectTo 时我才会遇到问题,它可以在其他方式下正常工作,使用相同的映射配置(也没有使用 Ignore)

以上是关于使用 Source 中的 NotMapped/Computed 属性映射到目标时的 Automapper ProjectTo<>() 问题的主要内容,如果未能解决你的问题,请参考以下文章

为啥source命令无法使用

WPF 中的image控件的Source如何赋值?

WPF 中的image控件的Source如何赋值?

idea 中的默认Java怎么改??? (请使用 -source 8 或更高版本以启用 lambda 表达式)

runloop中的source0和source1分别处理啥时间

如何使用scala.io.Source删除字节顺序标记?