My current code is working:
map.ForMember(x => x.Address, m => m.ResolveUsing(l => {
var engine = new MappingEngine((IConfigurationProvider)cfg);
var adress = engine.Map<AddressDto>(l.ContactInfo);
engine.Map(l.Address, adress);
return adress;
}));
but I thought there might be another way, something like:
map.ForMember(x => x.Address, m => m.MapFrom(x => x.ContactInfo));
map.ForMember(x => x.Address, m => m.MapFrom(x => x.Address));
But the last ForMember seems to override the existing map.
I'm trying to combine Address and ContactInfo properties into a single object on ListingDto.Address.
void Main()
{
var map = Mapper.CreateMap<Listing, ListingDto>();
var cfg = Mapper.Configuration;
map.ForMember(x => x.Address, m => m.ResolveUsing(l => {
var engine = new MappingEngine((IConfigurationProvider)cfg);
var adress = engine.Map<AddressDto>(l.ContactInfo);
engine.Map(l.Address, adress);
return adress;
}));
Mapper.CreateMap<Address, AddressDto>()
.ForMember(x => x.Latitude, x => x.Ignore());
Mapper.CreateMap<ContactInfo, AddressDto>()
.ForMember(x => x.Street, x=> x.Ignore());
Mapper.Map<ListingDto>(new Listing{
Name="Foo",
Address = new Address{Street = "Street"},
ContactInfo = new ContactInfo{ Latitude = "latitude"}}).Dump();
}
// Define other methods and classes here
public class Listing{
public string Name { get; set; }
public Address Address { get; set; }
public ContactInfo ContactInfo {get;set;}
}
public class ContactInfo{
public string Latitude {get;set;}
}
public class Address{
public string Street {get;set;}
}
public class AddressDto{
public string Latitude {get;set;}
public string Street {get;set;}
}
public class ListingDto{
public string Name { get; set; }
public AddressDto Address {get;set;}
}
.Dump() <-- is from linqpad to output
Something like this should work:
.ForMember(x => x.Address, o => o.MapFrom(
s => new AddressDto {
Latitude = s.ContactInfo.Latitude,
Street = s.Address.Street }));
Related
I have a PITA legacy DB model. Here is the relevant portion
class SalesOrder
{
public Recipient OrderBy {get;set;}
public Recipient BillTo {get;set;}
public List<SalesOrderLine> Lines {get;set;}
}
class SalesOrderLine
{
public Recipient ShipTo {get;set;}
public Address ShipToAddress {get;set;}
}
class Recipient
{
public Address DefaultAddress {get;set;}
}
Now comes the fun part.
class RecipientDTO {
public string Name {get;set;}
public string Address1 {get;set;}
public string Address2 {get;set;}
...
}
I have the OrderDTO that needs to be like this
new OrderDTO {
OrderBy = new RecipientDTO {
Name = OrderBy.Name,
Address1 = OrderBy.DefaultAddress.Addr1,
....
}
BillTo = new RecipientDTO {
Name = BillTo.Name,
Address1 = BilLTo.DefaultAddress.Addr1,
....
}
Lines = Lines.Select (l => new SalesOrderLineDTO {
ShipTo = new RecipientDTO {
Name = ShipTo.Name,
Address1 = l.ShipToAddress.Addr1, //NOTE. THIS IS NOT USING DEFAULT ADDRESS
....
}
} )
}
How do I write the mapping configuration in Auto Mapper for this to make sure the projection emits the chosen columns. If I use CustomResolver, the projection is not emitting the sql and it goes to the DB every time i access the address. Mucho sad!
To anyone who is interested , i have a backreference to Recipient from Address. SO I was able to do this following
m.CreateMap<SalesOrder, OrderDTO>()
.ForMember(d => d.OrderBy, conf => conf.MapFrom(o => o.OrderBy.DefaultAddress))
.ForMember(d => d.BillTo, conf => conf.MapFrom(o => o.BillTo.DefaultAddress));
m.CreateMap<Address, RecipientDTO>()
.ForMember(r => r.CustomerRecipientId, conf => conf.MapFrom(ra => ra.Recipient.CustRecipId))
.ForMember(r => r.Address1, conf => conf.MapFrom(ra => ra.Addr1))
.ForMember(r => r.Address2, conf => conf.MapFrom(ra => ra.Addr2))
....
m.CreateMap<SalesOrderLine, OrderLineDTO>()
.ForMember(l => l.OrderQuantity, conf => conf.MapFrom(l => l.OrderQty ?? 0))
.ForMember(l => l.ShipTo, conf => conf.MapFrom(z => z.ShipToAddress));
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 a Domain model that countains the following properties :
public class Profile
{
public DateTime birthDate { get; set; }
public List<Language> languages { get; set; }
}
Where Language represent an enumeration defined here :
public enum Language
{
English,
French,
Spannish
}
i would like to automatically populate the two following properties based on the languages values stored inside my domain model :
public Dictionary <int, String> languages_list { get; set; }
public List<string> languages_known { get; set; }
the question is : can it be done using automapper , if so how should i proceed ?
OK, using the detail you've provided this is what I've come up with.
Classes
public class SOProfile
{
public DateTime birthDate { get; set; }
public List<Language> languages { get; set; }
}
public class WhatAmI
{
public Dictionary<int, String> languages_list { get; set; }
public List<string> languages_known { get; set; }
}
Note that Profile was renamed to SOProfile to avoid a clash with AutoMapper
AutoMapper Configuration
public class MyProfile : Profile
{
public override string ProfileName
{
get
{
return "MyProfile";
}
}
protected override void Configure()
{
Mapper.CreateMap<SOProfile, WhatAmI>()
.ForMember(dest => dest.languages_list,
opt => opt.MapFrom(
src => src.languages
.Select((x,i) => new { Item = x, Index = i})
.ToDictionary(x => x.Index,
x => x.Item.ToString())))
.ForMember(dest => dest.languages_known,
opt => opt.MapFrom(
src => src.languages
.Select(x => x.ToString()).ToList()));
}
}
Unit Tests
[TestFixture]
public class MappingTests
{
[Test]
public void AutoMapper_Configuration_IsValid()
{
Mapper.Initialize(m => m.AddProfile<MyProfile>());
Mapper.AssertConfigurationIsValid();
}
[Test]
public void AutoMapper_Mapping_IsValid()
{
Mapper.Initialize(m => m.AddProfile<MyProfile>());
Mapper.AssertConfigurationIsValid();
var profile = new SOProfile
{
birthDate = new DateTime(2012, 01, 01),
languages = new List<Language>
{
Language.English,
Language.French,
Language.English,
Language.French
}
};
var rc = Mapper.Map<SOProfile, WhatAmI>(profile);
Assert.That(rc, Is.Not.Null);
Assert.That(rc.languages_known, Is.Not.Null);
Assert.That(rc.languages_known.Count, Is.EqualTo(4));
Assert.That(rc.languages_known.Count(x => x == "English"),
Is.EqualTo(2));
Assert.That(rc.languages_known.Count(x => x == "French"),
Is.EqualTo(2));
Assert.That(rc.languages_known.Count(x => x == "Spanish"),
Is.EqualTo(0));
Assert.That(rc.languages_list, Is.Not.Null);
Assert.That(rc.languages_list.Count, Is.EqualTo(4));
Assert.That(rc.languages_list.First(x => x.Key == 0).Value,
Is.EqualTo("English"));
Assert.That(rc.languages_list.First(x => x.Key == 1).Value,
Is.EqualTo("French"));
Assert.That(rc.languages_list.First(x => x.Key == 2).Value,
Is.EqualTo("English"));
Assert.That(rc.languages_list.First(x => x.Key == 3).Value,
Is.EqualTo("French"));
}
}
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);
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);
}