AutoMapper and convert a datetime to string - c#

I can't get my head round the following issue. I have a feeling it is a limitation of LINQ and expression trees, but not sure how to accept the lambda body. Can I achieve this WITHOUT creating a custom converter?
Mapper.CreateMap<I_NEWS, NewsModel>()
.ForMember(x => x.DateCreated, opt => opt.MapFrom(src => {
var dt = (DateTime)src.DateCreated;
return dt.ToShortDateString();
}));
I'm getting this error:
A lambda expression with a statement body cannot be converted to an expression tree

In order to use lambda bodies, use .ResolveUsing instead of .MapFrom.
As per the author:
MapFrom has some extra stuff that needs expression trees (like null
checking etc).
So your statement would look like this:
Mapper.CreateMap<I_NEWS, NewsModel>()
.ForMember(x => x.DateCreated, opt => opt.ResolveUsing(src => {
var dt = (DateTime)src.DateCreated;
return dt.ToShortDateString();
}));

try this:
Mapper.CreateMap<I_NEWS, NewsModel>().ForMember(x => x.DateCreated,
opt => opt.MapFrom(src => ((DateTime)src.DateCreated).ToShortDateString()));

If Nullable is destination then:
Mapper.CreateMap()
.ForMember(
dest => dest.StartDate,
opt => opt.MapFrom(
src => string.IsNullOrEmpty(src.StartDate) ? new DateTime?() : DateTime.ParseExact(src.StartDate, DATEFORMAT, CultureInfo.InvariantCulture)
)
)

Related

An expression tree may not contain an out argument variable declaration AutoMapper map from

I have the following mapping (AutoMapper Version="10.1.1"):
CreateMap<FooClass, BarClass>()
.ForMember(
dest => dest.Status,
opt => opt.MapFrom(
src => Enum.TryParse(src.Status ?? string.Empty, out Status result) ? result : Status.TEST_READY)
);
But got an error:
CS8198: An expression tree may not contain an out argument variable
declaration
Any idea how to accomplish this without needing to create a custom resolver?
Thanks
Try add src and destination (dest) in lambda.
CreateMap<FooClass, BarClass>()
.ForMember(
dest => dest.Status,
opt => opt.MapFrom(
(src, dest) => Enum.TryParse(src.Status ?? string.Empty, out Status result) ? result : Status.TEST_READY)
);

C# AutoMapper: set destination value by source value after validation in conditional mapping

I'm fairly new to AutoMapper and want to know how to set a destination member to a value based on a DIFFERENT source property value and if that value is null I just want to apply the default behaviour of Automapper (keep destination value when the source is null)
CreateMap<ClassA, ClassA>()
.ForMember(dest => dest.PropertyA, opt =>
opt.MapFrom(src => src.PropertyB!= null ? null : opt.UseDestinationValue())
)
This doesn't work (don't compile) the opt.UseDestinationValue() , what option can I use here?
Please help
Try setting a precondition for mapping destination property.
CreateMap<ClassA, ClassA>().ForMember(dest => dest.PropertyA, opt => opt.PreCondition((src, dest) => src.PropertyB != null));
This will map PropertyA only when PropertyB is not null. I did try a quick sample which gave the desired result.
I think you can use the PreCondition option For Mapping Property
CreateMap<ClassA, ClassA>()
.ForMember(dest => dest.PropertyA, opt => {
opt.PreCondition(src => src.PropertyB!= null);
opt.MapFrom(src => src.PropertyB);
});
Hope to help you
You can do as follows:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<ClassA,ClassA>()
.ForMember(dest => dest.PropertyA, opt => opt.Condition(src => (src.PropertyB!= null)));
});
Or as follows:
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<ClassA,ClassA>()
.ForMember(dest => dest.PropertyA, opt => {
opt.PreCondition(src => (src.PropertyB!=null));
opt.MapFrom(src => src.PropertyB); // mapping process takes place here
});
});
But the difference is that, the later runs sooner in the mapping process.
There is an excellent documentation in setting conditions for automapper:
https://docs.automapper.org/en/stable/Conditional-mapping.html

automapper conditional Custom Value Resolver

Is it possible to use a custom value resolver in automapper only if a certain condition is met?
In my case I only want to update the value with the custom value resolver if the destination is not null.
This is an example of my code. Basically I need to add the condition onto this. Is it possible?
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => op.ResolveUsing<ResponseXmlValueResolver>()
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name")))
I think Eris' solution would have work; It was just grammatical errors.
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => {
op.Condition(src => src != null);
op.ResolveUsing<ResponseXmlValueResolver>();
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name"));
});
Is this what you wanted?
If the destination is null, the mapping will be ignore.
If the destination is null, the mapping (with the customer resolved) will be apply.
As Conditions are evaluated after resolving member, like it's said here, none of the previous answers are correct.
You should rather use PreCondition this way:
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => {
op.PreCondition(src => src != null);
op.ResolveUsing<ResponseXmlValueResolver>();
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name"));
});
Will this work? (I don't have a windows box in front of me at the moment)
Mapper.CreateMap<ResponseXml, MyModel>()
.ForMember(dest => dest.FirstName,
op => op.Condition(src => src != null)
.ResolveUsing<ResponseXmlValueResolver>()
.FromMember(x => x.data.FirstOrDefault(y => y.name == "name")))

Replace Mapping with ConstructUsing

I make a mapping using Automapper 3.2.1:
Mapper.CreateMap<AvvisoPec, EsitiPostalizzazione>()
.ForMember(dst => dst.IDAvviso, src => src.MapFrom(v => EstraiIdAvviso(v)))
.ForMember(dst => dst.CodiceErrorePiattaforma, src => src.MapFrom(v => EstraiCodiceErrorePiattaforma(v.History)))
.ForMember(dst => dst.DescrizioneErrorePiattaforma, src => src.MapFrom(v => EstraiDescrizioneErrorePiattaforma(v.History)))
.ForMember(dst => dst.CodiceEsitoPostalizzazione, src => src.MapFrom(v => EstraiEsitoPostalizzazione(v.History)))
And this works good. now I want to remove parameterless construction in order to use a better encapsulation::
public EsitiPostalizzazione(int IDAvviso, int CodiceEsitoPostalizzazione, String CodiceErrorePiattaforma, String DescrizioneErrorePiattaforma)
{
this.IDAvviso = IDAvviso;
this.CodiceEsitoPostalizzazione = CodiceEsitoPostalizzazione;
this.CodiceErrorePiattaforma = CodiceErrorePiattaforma;
this.DescrizioneErrorePiattaforma = DescrizioneErrorePiattaforma;
}
so as far as I documented I should use the ConstructUsingafter the createMap() but I don't know how to use it, can someone help me?
All the example on the web will make something like:
Mapper.CreateMap<AvvisoPec, EsitiPostalizzazione>()
.ConstructUsing(x => new EsitiPostalizzazione(x.IdAvviso, ...))
but I have nothing like x.IDAvviso
The x in the ConstructUsing lambda should be your source type, which is AvvisoPec. So when you're using the EsitiPostalizzazione constructor you need to provide the values the way you were doing when using the MapFrom syntax.
Based on your code I would expect it to be:
Mapper.CreateMap<AvvisoPec, EsitiPostalizzazione>()
.ConstructUsing(v => new EsitiPostalizzazione(EstraiIdAvviso(v),
EstraiEsitoPostalizzazione(v.History),
EstraiCodiceErrorePiattaforma(v.History),
EstraiDescrizioneErrorePiattaforma(v.History)));

Extract AutoMapper source name from custom maps via ForMember

I have successfully extracted simple source/destination pairs from an existing Automapper TypeMap using this code:
private MemberInfo getSource(Type destinationType, string destinationPropertyname)
{
TypeMap map = Mapper.GetAllTypeMaps()
.Where(m => m.DestinationType.Equals(destinationType))
.First();
IEnumerable<PropertyMap> properties =
map.GetPropertyMaps()
.Where(p => p.DestinationProperty
.Name
.Equals(destinationPropertyname,
StringComparison.CurrentCultureIgnoreCase));
PropertyMap sourceProperty = properties.First();
IMemberGetter mg = sourceProperty.GetSourceValueResolvers()
.Cast<IMemberGetter>()
.First();
return mg.MemberInfo;
}
However, when I add custom column mappings like this:
Mapper.CreateMap<Customer, CustomerViewModel>()
.ForMember(dest => dest.Cell, opt => opt.MapFrom(src => src.CellPhone))
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.EmailAddress));
... the source part of the mapping is not available from GetSourceResolvers() that I can tell.
I appreciate any guidance you have.
Thank you.
-Jessy Houle
Is this along the lines of what you wanted?
var map = Mapper.FindTypeMapFor<Customer, CustomerViewModel>();
foreach( var propertMap in map.GetPropertyMaps() )
{
var dest = propertMap.DestinationProperty.MemberInfo;
var source = propertMap.SourceMember;
}

Categories