I have a class with double and double? and I want to round these values in the mapping process.
I created a small code sample to illustrate the problem that I am facing:
When I use AddTransform<double?> the transformation works for double? but does nothing for double.
When I add AddTransform<double> the transformation works as long as all nullable fields have a value.
When both transformers are added and at least one field is null I see following error:
AutoMapper.AutoMapperMappingException
Inner Exception 1:
InvalidOperationException: Nullable object must have a value.
Error mapping types.
Mapping types:
TestSource -> TestDestination
AutoMapperValueTransformer.TestSource -> AutoMapperValueTransformer.TestDestination
Type Map configuration:
TestSource -> TestDestination
AutoMapperValueTransformer.TestSource -> AutoMapperValueTransformer.TestDestination
Destination Member:
Prop3
Here the code to reproduce the issue
using System;
using AutoMapper;
namespace AutoMapperValueTransformer
{
internal static class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<TestSource, TestDestination>()
.AddTransform<double?>(t => t.HasValue ? Math.Round(t.Value, 3) : t)
//.AddTransform<double>(t => Math.Round(t, 3))
);
var mapper = config.CreateMapper();
var src = new TestSource()
{
Prop1 = 3.3454353,
Prop2 = 3.3454353,
Prop3 = null,
};
var dst = mapper.Map<TestSource, TestDestination>(src);
Console.WriteLine(dst.Prop1);
Console.WriteLine(dst.Prop2);
Console.WriteLine(dst.Prop3);
}
}
internal class TestSource
{
public double Prop1 { get; set; }
public double? Prop2 { get; set; }
public double? Prop3 { get; set; }
}
internal class TestDestination
{
public double Prop1 { get; set; }
public double? Prop2 { get; set; }
public double? Prop3 { get; set; }
}
}
Use this instead:
cfg.CreateMap<double, double>().ConvertUsing(source => Math.Round(source, 3));
Or try the MyGet build.
Related
What I want to do is to get the lists (plural) in MugDTO record to the list (singular) in Mug class. They ar both exactly the same except for the "top level".
But I can't figure out how it should work. I've tried with custom value resolver and a Custom type converter but to no avail. Unfortunately I deleted the code for the converter and the resolver so I can't add them here.
This is what I started with:
CreateMap<MugDTO, Mug>().ForMember(x => x.MugItems, m => m.MapFrom(s => s.Carriers));
On the front-end it looks like this:
public abstract class MugItem
{
public string TypeName { get; set; }
public string BackgroundColor { get; set; }
public string BorderColor { get; set; }
}
public class Cartridge : MugItem
{
}
public class Carrier : MugItem
{
}
public class Mug
{
public int Id { get; set; }
public List<MugItem> MugItems { get; set; }
}
On the back end it looks the same except for the mug has separate litst for the carrier and the cartridge.
public record MugDTO
{
public int Id { get; set; }
public List<CarrierDTO> Carriers { get; set; }
public List<CartridgeDTO> Cartridges { get; set; }
}
Error I get:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Error mapping types.
Mapping types:
MugDTO -> Mug
Test.Shared.Domain.MugDTO -> Test.Client.Domain.Mug
Type Map configuration:
MugDTO -> Mug
Test.Shared.Domain.MugDTO -> Test.Client.Domain.Mug
Destination Member:
MugItems
AutoMapper.AutoMapperMappingException: Error mapping types.
Mapping types:
MugDTO -> Mug
Test.Shared.Domain.MugDTO -> Test.Client.Domain.Mug
Type Map configuration:
MugDTO -> Mug
Test.Shared.Domain.MugDTO -> Test.Client.Domain.Mug
Destination Member:
MugItems
---> System.ArgumentException: Cannot create an instance of abstract type Test.Client.Domain.MugItem.
So the solution was that I needed two lines of code and to map and union the records inline.
CreateMap<MugItemDTO, MugItem>().IncludeAllDerived();
CreateMap<MugDTO, Mug>()
.ForMember(dear => dear.MugItems, opt => opt.MapFrom(
(src, dest, member, context) => context.Mapper.Map<List<CarrierDTO>>(src.Carriers).Union<MugItemDTO>(context.Mapper.Map<List<CartridgeDTO>>(src.Cartridges))));
When mapping a source instance to a destination instance, how are null source values handled? It appears that with built in types, a null source value will overwrite a destination value (set it to null). However with a navigation property, a destination value will not be set to null by a null source value, e.g. OuterDest.Innter:
`
public class OuterSource
{
public int Value { get; set; }
public InnerSource? Inner { get; set; }
}
public class InnerSource
{
public int OtherValue { get; set; }
}
public class OuterDest
{
public int Value { get; set; }
public InnerDest? Inner { get; set; }
}
public class InnerDest
{
public int OtherValue { get; set; }
}
`
This test will fail
[TestMethod]
public void NestedTestNullSourceValue()
{
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<OuterSource, OuterDest>();
cfg.CreateMap<InnerSource, InnerDest>();
});
var mapper = mappingConfig.CreateMapper();
OuterSource source = new()
{
Value = 123
};
OuterDest dest = new()
{
Value = 888,
Inner = new()
{
OtherValue = 999
}
};
mapper.Map(source, dest);
Assert.AreEqual(123, dest.Value);
Assert.IsNull(dest.Inner);
}
Had the same issue (AutoMapper - Map(source, destination) overwrite destination child object with null value from source via configuration), updating to AutoMapper 12.0.1 pre-release solved my issue
Having an issue with version 6.1.1. In the below, the result of the reverse map still has the Company object populated. Per this post, which shows what I am doing below, except they are ignoring a property, and I'm ignoring a complex object.
What am I missing?
CreateMap<Item, ItemViewModel>(MemberList.Destination)
.ReverseMap()
.ForMember(x => x.Company, x => x.Ignore())
;
With AutoMapper 6.1 you could use ForPath instead ForMember to ignore complex objects.
See How to ignore property with ReverseMap for further information.
I see not what is wrong, but here is a running sample:
namespace AutomapperTest2
{
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantEducation, ApplicantEducationVM>();
cfg.CreateMap<Applicant, ApplicantVM>().ReverseMap()
.ForMember(x => x.Education, x => x.Ignore());
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
Applicant ap = new Applicant
{
Name = "its me",
Education =
new ApplicantEducation
{
SomeInt = 10,
SomeString = "sampleString"
}
};
// Map
ApplicantVM apVm = Mapper.Map<Applicant, ApplicantVM>(ap);
Applicant apBack = Mapper.Map<ApplicantVM, Applicant>(apVm);
}
#endregion
}
/// Your source classes
public class Applicant
{
public ApplicantEducation Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducation
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
// Your VM classes
public class ApplicantVM
{
public string Description { get; set; }
public ApplicantEducationVM Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducationVM
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
}
}
I am trying to map two objects that are mostly similar with AutoMapper but one member (AudioSummary) raises the following exception :
The following property on EchoNestModel.AudioSummary cannot be mapped: AudioSummary
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type EchoNestModel.AudioSummary.
Context:
- Mapping to property AudioSummary from EchoNest.Api.AudioSummary to EchoNestModel.AudioSummary
- Mapping from type EchoNest.Api.TrackProfile to EchoNestModel.Profile
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
Mapping definition
var map = Mapper.CreateMap<TrackProfile, Profile>();
map.ForMember(dest => dest.ForeignIds, opt => opt.ResolveUsing<ForeignIdResolver>());
map.ForMember(dest => dest.ForeignReleaseIds, opt => opt.ResolveUsing<ForeignReleaseIdResolver>());
map.ForMember(s => s.Media, t => t.Ignore());
map.ForMember(s => s.ProfileId, t => t.Ignore());
map.ForMember(s => s.AudioSummary, t => t.MapFrom(s => s.AudioSummary));
I've added the following two lines but a totally different error occurs :
map.ForMember(s => s.AudioSummary.Profile, t => t.Ignore());
map.ForMember(s => s.AudioSummary.AudioSummaryId, t => t.Ignore());
Expression 's => s.AudioSummary.Profile' must resolve to top-level member and not any child object's properties.
Use a custom resolver on the child type or the AfterMap option instead.
Parameter name: lambdaExpression
How can I successfully map AudioSummary ?
Source object
Target object
EDIT: In general, try AutoMapper.Mapper.AssertConfigurationIsValid();, this will show you all possible problems in your mapper setup.
From the information you provided, it looks like you need to define map for the AudioSummary classes (dest and source) as well:
[TestFixture]
public class MappingTest
{
public class SourceAudioSummary
{
public int Id { get; set; }
public string OtherData { get; set; }
}
public class TrackProfile
{
public string Whatever { get; set; }
public SourceAudioSummary AudioSummary { get; set; }
}
public class DestAudioSummary
{
public int Id { get; set; }
public string OtherData { get; set; }
}
public class Profile
{
public string Whatever { get; set; }
public DestAudioSummary AudioSummary { get; set; }
}
[Test]
public void Mapping()
{
Mapper.CreateMap<SourceAudioSummary, DestAudioSummary>();
Mapper.CreateMap<TrackProfile, Profile>();
var trackProfile = new TrackProfile
{
Whatever = "something",
AudioSummary = new SourceAudioSummary
{
Id = 1,
OtherData = "other"
}
};
var profile = Mapper.Map<TrackProfile, Profile>(trackProfile);
Assert.That(profile.Whatever == "something");
Assert.That(profile.AudioSummary.Id == 1);
Assert.That(profile.AudioSummary.OtherData == "other");
}
}
I have used automapper for mapping lists in the past, for for some reason it won't work in this case.
public class MyType1 {
public int Id { get; set; }
public string Description { get; set; }
}
public class MyType2 {
public int Id { get; set; }
public string Description { get; set; }
}
public void DoTheMap() {
Mapper.CreateMap<MyType2, MyType1>();
Mapper.AssertConfigurationIsValid();
var theDto1 = new MyType2() { Id = 1, Description = "desc" };
var theDto2 = new MyType2() { Id = 2, Description = "desc2" };
List<MyType2> type2List = new List<MyType2> { theDto1, theDto2 };
List<MyType1> type1List = Mapper.DynamicMap<List<MyType1>>(type2List);
//FAILURE. NO EXCEPTION, BUT ZERO VALUES
List<MyType1> type1List2 =type2List.Select(Mapper.DynamicMap<MyType1>).ToList();
//SUCCESS, WITH LINQ SELECT
}
Change this:
Mapper.DynamicMap<List<MyType1>>(type2List)
To this:
Mapper.Map<List<MyType1>, List<MyType2>>(type2List);
DynamicMap is only if you don't know the type at compile time - for things like anonymous types.