Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoMapper: Ignore specific dictionary item(s) during mapping

Context:

There 2 main classes that look something like this:

public class Source
{
   public Dictionary<AttributeType, object> Attributes { get; set; }
}

public class Target
{
   public string Title { get; set; }
   public string Description { get; set; }
   public List<Attribute> Attributes { get; set; }
}

And the sub / collection / Enum types:

public class Attribute
{
   public string Name { get; set; }
   public string Value { get; set; }
}

public enum AttributeType
{
    Title,
    Description,
    SomethingElse,
    Foobar
}

Currently my Map looks like this:

 CreateMap<Source, Target>()
  .ForMember(dest => dest.Description, opt => opt.MapAttribute(AttributeType.Description))
  .ForMember(dest => dest.Title, opt => opt.MapAttribute(AttributeType.Title));

Where MapAttribute gets the item from the Dictionary and using the AttributeType I've provided, adds it to the target collection as a (name & value) object (using a try get and returning an empty if the key does not exist)...

After all of this my Target set end's up looking like this:

{ 
  title: "Foo", 
  attributes: [
     { name: "SomethingElse", value: "Bar" }, 
     { name: "Title", value: "Foo"}
  ] 
}


Question:

How do I go about mapping the rest of the items to the target class, but I need to be able to exclude specific keys (like, title or description). E.G. Source.Attribute items that have a defined place in target gets excluded from the Target.Attributes collection, and "left-over" properties still go to Target.Attributes.

For even more clarity (if my source looks like this):

{ attributes: { title: "Foo", somethingelse: "Bar" } }

it would map to a target like this:

{ title: "Foo", attributes: [{ name: "SomethingElse", value: "Bar" }] }

I've attempted this, but it does not compile, stating the following:

Custom configuration for members is only supported for top-level individual members on a type.

CreateMap<KeyValuePair<AttributeType, object>, Attribute>()
   .ForSourceMember(x => x.Key == AttributeType.CompanyName, y => y.Ignore())
like image 649
Rohan Büchner Avatar asked Oct 17 '22 05:10

Rohan Büchner


1 Answers

It is possible to prefilter values on the mapping configuration, that some values would even not go to the target I'm using automapper v6.1.1, there are some difference, but the idea should be the same)

To make things works I have to add KeyValuePair<AttributeType, object> to Attribute mapping first (MapAttribute just returns dictionary value)

CreateMap<KeyValuePair<AttributeType, object>, Attribute>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(s => s.Key))
    .ForMember(dest => dest.Value, opt => opt.MapFrom(s => s.Value));

As it's defined which attributes should be ignored, they will goes to ignore list, based on which mapping will filter out excess attributes

var ignoreAttributes = new[] {AttributeType.Description, AttributeType.Title};
CreateMap<Source, Target>()
    .ForMember(dest => dest.Description, opt => opt.MapFrom(s => s.MapAttribute(AttributeType.Description)))
    .ForMember(dest => dest.Title, opt => opt.MapFrom(s => s.MapAttribute(AttributeType.Title)))
    .ForMember(dest=>dest.Attributes, opt=> opt.MapFrom(s=>s.Attributes
        .Where(x=> !ignoreAttributes.Contains(x.Key))));

Based on the final mapping example

var result = Mapper.Map<Target>(new Source
{
    Attributes = new Dictionary<AttributeType, object>
    {
        {AttributeType.Description, "Description"},
        {AttributeType.Title, "Title"},
        {AttributeType.SomethingElse, "Other"},
    }
});

result will have filled Titel, Description and just one attribute SomethingElse

enter image description here enter image description here

like image 190
ASpirin Avatar answered Nov 15 '22 07:11

ASpirin