Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoMapper ProjectTo fails with FirstOrDefault

I am using AutoMapper ProjectTo to map Person entities to EmployeeDto. In my EmployeeDto i have a single AddressDto property which i want to capture. Becuase the Person has a collection of Address i defined my mapping to capture the first one:

.ForMember(d => d.Address, s => s.MapFrom(m => m.Addresses.FirstOrDefault())

The problem is that when i include this in my mapping i receive an error that the "Specified type member "IsDeleted" is not supported..." IsDeleted is a base property on Person with the attribute [NotMapped]. My EmployeeDto does not include the IsDeleted propery.

When i look at the IQueryable expression property i get this:

System.Data.Entity.Core.Objects.ObjectQuery`1[MyProject.Data.Core.Entities.Person]
.MergeAs(MergeOption.AppendOnly)
.Where(
    // Quoted to induce a closure:
    a => a.FirstName.Contains(param.Value))
.Select(
    // Quoted to induce a closure:
    dto => new 0_Culture=neutral_PublicKeyToken=null>
    {
        IsDeleted = dto.IsDeleted,
        UpdateDate = dto.UpdateDate,
        CreateDate = dto.CreateDate,
        Id = dto.Id,
        …remaining Person entity properties removed for brevity,
        Addresses = dto.Addresses,
        Address = dto.Addresses.FirstOrDefault()
    })
.Select(

    // Quoted to induce a closure:
    dto => new EmployeeDto
    {
        …all employeedto property removed for brevity,
        Address = (dto.Address != null) ? new AddressDto { Id = dto.Address.Id } : null
    })

Here is my mapping (the ForSourceMember() portion is something i tried, but it failed to resolve the issue- same result with or without):

configuration.CreateMap<Person, EmployeeDto>()
            .ForSourceMember(s => s.IsDeleted, x => x.Ignore())
            .ForSourceMember(s => s.CommonProperties, x => x.Ignore())
            .ForSourceMember(s => s.Events, x => x.Ignore())
            .ForMember(d => d.PersonId, s => s.MapFrom(m => m.Id))
            .ForMember(d => d.PersonType, s => s.UseValue(PersonTypes.Employee))                
            .ForMember(d => d.StatusType, s => s.MapFrom(m => m.PersonStatusType.Type))
            .ForMember(d => d.Status, s => s.MapFrom(m => m.PersonStatusType.Code))
            .ForMember(d => d.Address, s => s.MapFrom(m => m.Addresses.FirstOrDefault()))
            ;

My AddressDto has a similar CreateMap definition for Address to AddressDto.

Further, when these same objects are children of another dto object the mapping works. Hopefully this is enough for someone to recognize my problem.

EDIT Workaround- adding a conditional will work. For some reason it prevents the IQueryable expression from creating the .Select(dto => new 0_Culture=neutral_publickkeytoken=null> block:

.ForMember(d => d.Address, s => s.MapFrom(m => m.Addresses.Count > 0 ? m.Addresses.FirstOrDefault() : null)
like image 349
chadb Avatar asked Nov 07 '22 11:11

chadb


1 Answers

This is an EF Core issue, which has been fixed in EF Core 3.0. More details about the issue here: https://github.com/dotnet/efcore/issues/15399.

As a workaround, you can use .Take(1) and project into an ICollection.

like image 121
Marius Stănescu Avatar answered Dec 26 '22 13:12

Marius Stănescu