Automapper mapping function

2019-09-18 20:26发布

问题:

Hello everyone I am trying to achieve something relatively simple in Automapper but I keep on failing. I am trying to map a property on my Dto with a function expression

   .ForMember(dest => dest.HasPaid, opt => opt.MapFrom(c => MapHasPaid(c)))

    private bool MapHasPaid(AppUser src)
    {
        var lastPayment = src.RentPayments.OrderByDescending(p => p.DateTo).FirstOrDefault();
        if (lastPayment == null)
        {
            return false;
        }

        return lastPayment.DateFrom.Date <= DateTime.Now.Date &&
               DateTime.Now.Date <= lastPayment.DateTo.Date;
    }

This returns

LINQ to Entities does not recognize the method 'Boolean MapHasPaid(xxxx.Models.AppUser)' method, and this method cannot be translated into a store expression.

I tried with a IValueResolver

public class CustomResolver : IValueResolver<AppUser, HouseMateEntity, bool>
{
    public bool Resolve(AppUser source, HouseMateEntity destination, bool member, ResolutionContext context)
    {
        var lastPayment = source.RentPayments.OrderByDescending(p => p.DateTo).FirstOrDefault();
        if (lastPayment == null)
        {
            return false;
        }

        return lastPayment.DateFrom.Date <= DateTime.Now.Date &&
               DateTime.Now.Date <= lastPayment.DateTo.Date;
    }
}


.ForMember(dest => dest.HasPaid, opt => opt.ResolveUsing<CustomResolver>())

but give me the following exception

Can't resolve this to Queryable Expression

How can I map my property as a result of more complex query?

thanks

I have also tried the simple case on the documentation https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers

same error exception

Can't resolve this to Queryable Expression

Has anyone got ResolveUsing to work? is this an automapper bug?

EDIT: the only way I could get this working is with the following, and it really smells bad to me

.ForMember(dest => dest.HasPaid,
                   opt => opt.MapFrom(c => DbFunctions.TruncateTime(c.RentPayments.OrderByDescending(p => p.DateTo).FirstOrDefault().DateFrom) <= DbFunctions.TruncateTime(DateTime.Now) &&
                                      DbFunctions.TruncateTime(DateTime.Now) <= DbFunctions.TruncateTime(c.RentPayments.OrderByDescending(p => p.DateTo).FirstOrDefault().DateTo)));

回答1:

I expect you are attempting to use this with AutoMapper's Projection? The issue is that the Resolve functions you are attempting to implement are making calls that are not translatable in LINQ to Entities. Basically LINQ does not have a mapping between one of the C# calls you're making to some corresponding SQL to run on the database.

The reason your last bit of code works is because you are using the DbFunctions class which implements all the necessary logic to handle datetime manipulation in LINQ to Entities or LINQ to SQL.

Either use the DbFunctions methods exactly like you are or first retrieve the data (call .ToList() or similar on the query) and then map to your Target type.

This is documented in the AutoMapper documentation.

EDIT:

If you are using the AutoMapper Queryable Extensions then ResolveUsing is NOT supported AutoMapper documentation: Supported mapping options



标签: AutoMapper