简体   繁体   中英

AutoMapper to Map with nested property of List of array

I have a class A that I need to Map with class C

public class A{
   public string Name {get; set;}
   public List<B> Address {get; set;}}


public class B{
    public string streetName{get; set;}
    public int Number {get; set;}}

I need to map this to Class C

public class C{
    public int id{get;set;}
    public List<MapAddressData> MapAddressData{get; set;}
    public DateTime DateCreated {get; set;}}

public class MapAddressData{
   public string Name {get; set;}
   public string streetName {get; set;}
   public int Number{get; set;}}

SO I need to use automapper to map List of Address in class A to List of MapAddressData in class C. I dont understand How to use for loop for map this because for a particular Name we well be having many address, n that has to mapped one by one to 'MapAddressData' class in class C. Is there way to implement this?

I'm not sure if there's a cleaner way to do this, but you can configure AutoMapper like this:

cfg.CreateMap<A, C>()
    .BeforeMap((src, _, ctx) => ctx.Options.Items[$"__{nameof(A)}"] = src)
    .ForMember(dst => dst.MapAddressData, mo => mo.MapFrom(src => src.Address));

cfg.CreateMap<B, MapAddressData>()
    .ForMember(dst => dst.Name, mo => mo.MapFrom(new MapAddressNameResolver()));

With .BeforeMap we are storing the source value in the mapping context items collection using the key "__A" .

Next, we tell AutoMapper that it should use the Address property on the source object to map MapAddressData on the destination object.

Then we create a map between B and MapAddressData . For the Name property of the destination object, we use a custom resolver: MapAddressNameResolver to retrieve the value:

private class MapAddressNameResolver : IValueResolver<B, MapAddressData, string>
{
    public string Resolve(B source, MapAddressData destination, string destMember, ResolutionContext context)
    {
        if (context.Options.Items.TryGetValue($"__{nameof(A)}", out var parent) 
                && parent is A sourceParent)
        {
            return sourceParent.Name;
        }
        throw new InvalidOperationException("Address cannot be mapped to MapAddressData indepdendently of A to C.");
    }
}

This looks at the mapping context items collection and tries to retrieve the value stored against the key "__A" . If it is indeed an instance of A , we return its Name property.

If any of these are incorrect, we throw an exception so that we know this has failed if someone tries to use this mapping in some other context in future without adding the appropriate support.


Full example

public static void TestMethod()
{

    var mapperConfig = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<A, C>()
            .BeforeMap((src, _, ctx) => ctx.Options.Items[$"__{nameof(A)}"] = src)
            .ForMember(dst => dst.MapAddressData, mo => mo.MapFrom(src => src.Address));

        cfg.CreateMap<B, MapAddressData>()
            .ForMember(dst => dst.Name, mo => mo.MapFrom(new MapAddressNameResolver()));
    });
    var mapper = mapperConfig.CreateMapper();

    var sourceObject = new A()
    {
        Name = "bob",
        Address = new List<B>()
    {
        new B()
        {
            Number = 5,
            streetName = "bob street"
        },
        new B()
        {
            Number = 125,
            streetName = "somewhere street"
        }
    }
    };

    var mappedObject = mapper.Map<C>(sourceObject);
}

private class MapAddressNameResolver : IValueResolver<B, MapAddressData, string>
{
    public string Resolve(B source, MapAddressData destination, string destMember, ResolutionContext context)
    {
        if (context.Options.Items.TryGetValue($"__{nameof(A)}", out var parent) && parent is A sourceParent)
        {
            return sourceParent.Name;
        }
        throw new InvalidOperationException("Address cannot be mapped to MapAddressData indepdendently of A to C.");
    }
}

For example you have data for class A like

Name=User1

Address1

streetName = streetName1 Number = Number1

Address2

streetName = streetName2 Number = Number2

Address3

streetName = streetName3 Number = Number3

For multiple Names you have to make nested loop to access each and every data in class A object like, meanwhile consider we have object of class MapAddressData as mappedAddress.

foreach(var item from ClassAObject)
{
   mappedAddress.Name = item.Name;
   foreach(var address from item.Address)
   {
      mappedAddress.streetName = address.streetName;
      mappedAddress.Number= address.Number;
      >> Here you will be adding this mappedAddress objcect in mappedAddress from class C.

   }
}

Attaching code sample just to get more clarification

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM