Harish S
Harish S

Reputation: 79

Automapper ProjectTo<>() issue when using NotMapped/Computed property in Source to map in destination

I am getting the error only when I am using ProjectTo, I could not understand the underlying issue. (Automapper version am using 4.2.1.0) "The specified type member 'Tags' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported." We could do this manipulation in DTO as well, but I should stick to doing the manipulation in entity side only. Do let me know the ways or work around I could handle this without upgrading the version. TIA I want the computed value of Tags property of entity needs to be mapped to the Tags property of the DTO, but this works fine when I am doing the normal way.

Source/destination types

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); }
        }       
    }

Entity Config

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");
                });
        }
    }

Destination DTO:

public class Template
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public List<string> Tags { get; set; }
    }

Mapping configuration

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;
                }
            }

Upvotes: 2

Views: 3443

Answers (2)

lxalln
lxalln

Reputation: 930

This is happening because .ProjectTo<>(...) is building the select statement for you in SQL. As .Tags maps between your objects, it is being included in the select statement, and then entity framework is complaining that it can't do that (because of the NotMapped attribute).

Instead of using ProjectTo<>(...) you could just use a normal .ToList() and then use .Select(Mapper.Map<TDto>) or Mapper.Map<List<TDto>>(list).

That should work, as entity framework will populate your Tags field from the string field, and automapper can do the map ok.

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;
    }
}

Upvotes: 4

Kevin R.
Kevin R.

Reputation: 3591

Have you tried implementing the ignore in the MappingConfiguration? I can't quite tell which direction you're having issues with, but something like:

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

Upvotes: 0

Related Questions