Automapper - an expression tree lambda cannot contain a null propagating operator - automapper

I have a following mapping in Automapper
.ForMember(d => d.Comment1, opt => opt.MapFrom(src => src.Comments?.FirstOrDefault(c => c.Type == "COMMENT1")?.Text))
But I am able to figure a way around the error
an expression tree lambda cannot contain a null propagating operator
Is there a simple way for doing this?

Related

Automapper 8 - An expression tree lambda may not contain a null propagating operator

Automapper version 8.0.0 removed ResolveUsing extension method from IMappingExpression and consolidated it with MapFrom extension method. However, after replacing ResolveUsing with MapFrom method, certain configurations throw exception.
Original ResolveUsing:
CreateMap<SourceType, DestinationType>()
.ForMember(dest => dest.Customer,
opt => opt.ResolveUsing(src => src?.Customer ?? new Customer())
);
Replaced with MapFrom:
CreateMap<SourceType, DestinationType>()
.ForMember(dest => dest.Customer,
opt => opt.MapFrom(src => src?.Customer ?? new Customer())
);
This produces compilation error:
Error CS8072
Automapper
An expression tree lambda may not contain a null propagating operator.
New Func-based overloads in Automapper 8.0.0 accept more parameters compared to old/removed ResolveUsing overloads.
Instead of using lambda expression with single input parameter opt.MapFrom(src => ...) when replacing ResolveUsing, overload with 2 parameters should be used opt.MapFrom((src, dest) => ...).
MapFrom expression becomes:
opt => opt.MapFrom((src, dest) => src?.Customer ?? new Customer())
Full example:
CreateMap<SourceType, DestinationType>()
.ForMember(dest => dest.Customer,
opt => opt.MapFrom((src, dest) => src?.Customer ?? new Customer())
);

AutoMapper specific value for a property

I'm trying to map List to List but one of the properties of the DtoLineItem is different.
How can I tell AutoMapper to map a specific property of LineItem to that Property?
I've tried this:
CreateMap<Cart, CartModel>()
.ForMember(dest => dest.LineItems, opt => opt.MapFrom(src => src.LineItems))
.ForMember(dest => dest.LineItems.Select(x => x.CustomField),
opt => opt.MapFrom(src => src.LineItems.Select(y => y.Description)));
AutoMapper.AutoMapperConfigurationException Custom configuration for
members is only supported for top-level individual members on a type.

How to Map String Literal to Destination Property

I'd like to be able to do something like this using automapper:
Mapper.CreateMap<Source, Destination>()
.ForMember<d => d.Member, "THIS STRING">();
I'd like d.Member to always be "THIS STRING" and not be mapped from any particular member from the source model. Putting a string field in the source model with "THIS STRING" as it's value is also not an option.
Does AutoMapper support these kinds of things in any way?
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Member, opt => opt.UseValue<string>("THIS STRING"));
Starting with version 8.0 you have to use the following:
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Member, opt => opt.MapFrom(src => "THIS STRING"));

Conditionally overwrite destination if no destination object is used

I would like to setup an Automapper mapping that follows the following rules.
If the "in place" destination syntax is not used, map a particular member to a value
If an object is passed in, then use the destination value
I've tried this every way I can think of. Something like this:
Mapper.CreateMap<A, B>()
.ForMember(dest => dest.RowCreatedDateTime, opt => {
opt.Condition(dest => dest.DestinationValue == null);
opt.UseValue(DateTime.Now);
});
This always maps the value. Essentially what I want is this:
c = Mapper.Map<A, B>(a, b); // does not overwrite the existing b.RowCreatedDateTime
c = Mapper.Map<B>(a); // uses DateTime.Now for c.RowCreatedDateTime
Note: A does not contain a RowCreatedDateTime.
What are my options here? It's very frustrating since there appears to be no documentation on the Condition method, and all the google results seem to be focused around where the source value is null, rather than the destination.
EDIT:
Thanks to Patrick, he got me on the right track..
I figured out a solution. If anyone has a better way of doing this, please let me know. Note I had to reference dest.Parent.DestinationValue rather than dest.DestinationValue. For some reason, dest.DestinationValue is always null.
.ForMember(d => d.RowCreatedDateTime, o => o.Condition(d => dest.Parent.DestinationValue != null))
.ForMember(d => d.RowCreatedDateTime, o => o.UseValue(DateTime.Now))
I believe you need to set up two mappings: one with the Condition (which determines IF a mapping should be performed) and one which defines what to do if the Condition returns true. Something like this:
.ForMember(d => d.RowCreatedDateTime, o => o.Condition(d => d.DestinationValue == null);
.ForMember(d => d.RowCreatedDateTime, o => o.UseValue(DateTime.Now));

Getting destination member expression by source expression in AutoMapper

Let say I have mapping like this:
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
Is it possible to get back the destination member expression by its relative source expression. Something like this:
Expression<Func<CalendarEventForm, object>> destinationExpression = Mapper.GetMemmberExpression(src => src.EventDate.Date);
Thanks in advance.

Resources