AutoMapper property with TypeName start with Tbl in Source and property start with FK in destination - automapper

i have problem with automapper for convert property with TypeName start with Tbl in Source and property start with FK in destination, but this error occured:
Missing type map configuration or unsupported mapping. Tbl_Child -> Int32 Tbl_Child -> System.Int32 Destination path: Destination.FK_Child.FK_Child Source value: Tbl_Child
code is:
public class Source
{
public Tbl_Child Child { get; set; }
public string SourceName { get; set; }
}
public class Tbl_Child
{
public int ID_Child { get; set; }
public string ChildName { get; set; }
}
public class Destination
{
public int FK_Child { get; set; }
public string ChildName { get; set; }
public string SourceName { get; set; }
}
static void Main(string[] args)
{
var src = new Source()
{
Child = new Tbl_Child()
{
ChildName = "ch",
ID_Child = 1
},
SourceName = "src"
};
AutoMapper.Mapper.CreateMap<Source, Destination>();
var dest = AutoMapper.Mapper.Map<Source, Destination>(src);
Console.ReadKey();
}
i test AutoMapper version 2.0.0.0, and 3.1.1.0

Out of the box, Automapper is able to map properties with the same name and flattening in properties. For other properties you must specify the mapping that you want.
For example mapping from Source.Child.ID_Child to Destination.FK_Child would be accomplished like this:
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.FK_Child, o => o.MapFrom(s => s.Child.ID_Child));
And you can chain all your custom mappings like so
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.FK_Child, o => o.MapFrom(s => s.Child.ID_Child))
.ForMember(d => d.ChildName, o => o.MapFrom(s => s.Child.ChildName));
This mapping should be enough in this case but I think you may need to read the wiki a bit before going on

Related

Automapper - need to map a parent property when mapping children

The basic problem is that I have properties on my source parent entities that I need to map to my destination child DTOs when the child entities are mapped to the destination child DTOs. The child entities do not have navigation properties to their parent. As you can see below, the parent has a field of the child type. You can see in the code below that ParentSource has a property called TitleImage of type ImageSource. So linkage is one directional. In my code, I use the ImageSource everywhere. There are at least 15 different entities with properties of type ImageSource. I normalized my database. The ImageSource entity does not have X and Y coordinates because only a few, maybe 5 out of 30, places actually need those extra values. In those few places, I added the X and Y on the parent as you see on the ParentSource class with the TitleImageX and TitleImageY.
I could easily do what I want with a loop, and that is what I am doing now, but I would love to use Automapper for this if I could. It would be way less code.
Here are my classes:
public class ParentSource
{
public string Id { get; set; }
public string Name { get; set; }
public ImageSource HomeImage { get; set; }
public ImageSource TitleImage { get; set; }
//These should be copied to the child object
public int TitleImageX { get; set; }
public int TitleImageY { get; set; }
}
public class ParentDest
{
public string ParentId { get; set; }
public string DisplayName { get; set; }
public ImageDest HomeImage { get; set; }
public ImageDest TitleImage { get; set; }
}
public class ImageSource
{
public string Id { get; set; }
public string Url { get; set; }
public decimal Height { get; set; }
public decimal Width { get; set; }
}
public class ImageDest
{
public string ImageId { get; set; }
public string ImageUrl { get; set; }
public decimal Height { get; set; }
public decimal Width { get; set; }
//Not all images have Coordinate
public decimal XCoordinate { get; set; }
public decimal YCoordinate { get; set; }
}
Here are what I have so far for doing the mappings. I don't know how to copy the X & Y coordinates from the parent to the child.
CreateMap<ParentSource, ParentDest>()
.ForMember(
dest => dest.ParentId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.DisplayName,
opt => opt.MapFrom(src => src.Name));
CreateMap<ImageSource, ImageDest>()
.ForMember(
dest => dest.ImageId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.ImageUrl,
opt => opt.MapFrom(src => src.Url));
Thank you for your time and help.
Or you can do it in config (not tried or tested) which you may prefer, but there is no check for null on TitleImage
CreateMap<ParentSource, ParentDest>()
.ForMember(
dest => dest.ParentId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.DisplayName,
opt => opt.MapFrom(src => src.Name))
.ForMember(dest=>dest.TitleImage,
opt=>opt.MapFrom(src=>new ImageDest
{
Width = src.TitleImage.Width,
Height = src.TitleImage.Height,
ImageId = src.TitleImage.Id,
ImageUrl = src.TitleImage.Url,
XCoordinate = src.TitleImageX,
YCoordinate = src.TitleImageY
}
));
From what I gather you want the X&Y from the parent into the child. You can use a converter class for this.
Converter:
public class ParentConverter : ITypeConverter<ParentSource, ParentDest>
{
public ParentDest Convert(ParentSource source, ParentDest destination, ResolutionContext context)
{
if (destination == null)
destination = new ParentDest();
destination.DisplayName = source.Name;
destination.ParentId = source.Id;
if (source.TitleImage != null)
{
destination.TitleImage = context.Mapper.Map<ImageDest>(source.TitleImage);
destination.TitleImage.XCoordinate = source.TitleImageX;
destination.TitleImage.YCoordinate = source.TitleImageY;
}
return destination;
}
}
Mapping:
CreateMap<ParentSource, ParentDest>()
.ConvertUsing<ParentConverter>();
CreateMap<ImageSource, ImageDest>()
.ForMember(
dest => dest.ImageId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.ImageUrl,
opt => opt.MapFrom(src => src.Url);
});

Automapper with different type of nested objects

I have a destination class like below
public class Order
{
public string OrderId { get; set; }
public List<Delivery> Deliveries { get; set;}
}
public class Delivery
{
public string ProductName { get; set; }
}
I've to map ProductName in above class from below source class
public class OrderDTO
{
public string OrderId { get; set; }
public List<OrderDelivery> Deliveries { get; set; }
}
public class OrderDelivery
{
public List<OrderDeliveryDetails> ProductDeliveryDetails { get; set; }
}
public class OrderDeliveryDetails
{
public string ProductName { get; set; }
}
How I can do this using Automapper.
(Note: Please Don't confuse with the List<OrderDeliveryDetails> in OrderDeliveryclass. It is because, it may have child products as well, but i need to take parent ProductNameonly)
Well if by taking the parent ProductName you mean the first, you can try something along these lines (otherwise I might've misunderstood your question and you can ignore my answer):
public class AutoMapperConfiguration : Profile
{
private readonly IConfiguration _mapper;
public AutoMapperConfiguration(IConfiguration mapper)
{
_mapper = mapper;
}
protected override void Configure()
{
_mapper.CreateMap<OrderDTO, Order>
.ForMember(d => d.OrderId, o => o.MapFrom(s => s.OrderId))
.ForMember(d => d.Deliveries, o => o.MapFrom(s => s.Deliveries));
_mapper.CreateMap<OrderDelivery, Delivery>
.ForMember(d => d.ProductName, o => o.MapFrom(s => s.ProductDeliveryDetails.First().ProductName));
}
}
and then simply
_mapper.Map(orderDto);
(this is, provided you have all the wireup needed to use AutoMapper).

What expression is needed for mapping this property?

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");
}
}

Can AutoMapper implicitly flatten this mapping?

I am trying to map between two lists of objects. The source type has a complex property of type A; the destination type is a flattened subset of type A plus an additional scalar property that is in the source type.
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Source
{
public A MyA { get; set; }
public int SomeOtherValue { get; set; }
}
public class Destination
{
public string Name { get; set; }
public int SomeOtherValue { get; set; }
}
If it's not clear, I'd like Source.MyA.Name to map to Destination.Name and Source.SomeOtherValue to map to Destination.SomeOtherValue.
In reality, type A has a dozen or so properties, about which 80% map over to properties of the same name in Destination. I can get things to work if I explicitly spell out the mappings in CreateMap like so:
CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.MyA.Name));
The downside here is I want to avoid having to add a ForMember line for each of A's properties that need to get copied over to Destination. I was hoping I could do something like:
CreateMap<Source, Destination>()
.ForMember(dest => dest, opt => opt.MapFrom(src => src.MyA));
But if I try the above I get a runtime error when the mapping is registered: "Custom configuration for members is only supported for top-level individual members on a type."
Thanks
create mappings between A and Destination, and Source and Destination, and then use AfterMap() to use first mapping in second
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
.AfterMap((s, d) => Mapper.Map<A, Destination>(s.MyA, d));
then use it like this:
var res = Mapper.Map<Source, Destination>(new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } });
As a workaround you can use custom type converter with additional property in the destination type to avoid recursion.
[TestFixture]
public class MapComplexType
{
[Test]
public void Map()
{
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>().ConvertUsing(new TypeConvertor());
var source = new Source
{
MyA = new A
{
Name = "Name"
},
SomeOtherValue = 5
};
var dest = new Destination();
Mapper.Map(source, dest);
Assert.AreEqual(dest.Name, "Name");
}
}
public class TypeConvertor : ITypeConverter<Source, Destination>
{
public Destination Convert(ResolutionContext context)
{
var destination = (Destination) context.DestinationValue;
if (!((Destination)context.DestinationValue).IsMapped || destination == null)
{
destination = destination ?? new Destination();
destination.IsMapped = true; // To avoid recursion
Mapper.Map((Source)context.SourceValue, destination);
destination.IsMapped = false; // If you want to map the same object few times
}
Mapper.Map(((Source)context.SourceValue).MyA, destination);
return (Destination)context.DestinationValue;
}
}
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Source
{
public A MyA { get; set; }
public int SomeOtherValue { get; set; }
}
public class Destination
{
public string Name { get; set; }
public int SomeOtherValue { get; set; }
// Used only for mapping purposes
internal bool IsMapped { get; set; }
}
Try this,
Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
.ForMember(destination => destination.Name, options => options.MapFrom(source => Mapper.Map<A, Destination>(source.MyA).Name));
var objSource = new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } };
var result = Mapper.Map<Source, Destination>(objSource);

Automapping a wrapped class

We want to generically map from one database's context.tableType to another
e.g. UatDb.Branch--LiveDb.Branch
The table's are identical so no MapFrom is necessary.
The following generic mapping definition is sufficient
Mapper.CreateMap<TFromContextTableType,TToContextTableType>();
However!!!
We need to wrap the source context.tableType in the following wrapper class:
public class SWrapper<TFrom> where TFrom : class
{
public SWrapper(TFrom model)
{
Model = model;
}
public TFrom Model { get; private set; }
}
Now to perform the mapping we have to map as follows:
Mapper.CreateMap<SWrapper<FromBranchType>, ToBranchType>().ConstructUsing(x => new Live.Branch()))
.ForMember(d => d.BranchID, o => o.MapFrom(x => x.Model.BranchID))
.ForMember(d => d.BranchName, o => o.MapFrom(x => x.Model.BranchName))
.ForMember(d => d.BranchCountry, o => o.MapFrom(x => x.Model.BranchCountry))
This means that we cannot generically map and have to explicitly declare a ForMember for each mapping. I can't figure out any solution using Resolvers or Type Converters.
I thought about perhaps wrapping the target in a SWrapper then resolving the returned Wrapped object to return the internal Product but not sure how to perform this.
All ideas welcome....
Taking from your mapping you could do the following which does not require you to enter all of the property mappings by hand. The helper method in the second test allows for specialized mapping that you may not need.
[TestMethod]
public void Given_a_wrapper_class_I_should_be_able_to_unwrap_it_and_continue_with_mappings()
{
Mapper.CreateMap<Wrapper<Source>, Destination>()
.UnwrapUsing<Wrapper<Source>, Source, Destination>(w => w.Model)
.ForMember(d => d.Bar, o => o.MapFrom(d => d.Baz));
var source = new Source { Baz = 1, Foo = "One" };
var wrapped = new Wrapper<Source> { Model = source };
var destination = Mapper.Map<Destination>(wrapped);
Assert.IsNotNull(destination);
Assert.AreEqual(1, destination.Bar);
Assert.AreEqual("One", destination.Foo);
}
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDest> UnwrapUsing<TWrapper, TSource, TDest>(this IMappingExpression<TWrapper, TDest> expr, Func<Wrapper<TSource>, TSource> unwrapper)
{
Mapper.CreateMap<Wrapper<TSource>, TDest>()
.ConstructUsing(x => Mapper.Map<TSource, TDest>(unwrapper(x)));
return Mapper.CreateMap<TSource, TDest>();
}
}
public class Source
{
public string Foo { get; set; }
public int Baz { get; set; }
}
public class Destination
{
public string Foo { get; set; }
public int Bar { get; set; }
}
public class Wrapper<T>
{
public T Model { get; set; }
}
How about adding a little utility method to your wrapper class:
public TDest MapTo<TDest>()
{
return Mapper.Map<TFrom, TDest>(this.Model);
}

Resources