开发者

Why is AutoMapper only using my configuration on the first element in a list?

开发者 https://www.devze.com 2023-02-17 01:24 出处:网络
Here is my AutoMapper configuration: Mapper.CreateMap<Source, Destination>() .ConstructUsing(s => new Destination(s.CreatedDate.DateTime));

Here is my AutoMapper configuration:

Mapper.CreateMap<Source, Destination>()
      .ConstructUsing(s => new Destination(s.CreatedDate.DateTime));

Both classes have a single property, CreatedDate, but they are of different types:

public class Source
{
   public DateTimeOffset CreatedDate { get; set; }
}

public class Destination
{
   public Destination(DateTime created) { CreatedDate = created; }
   public DateTime CreatedDate { get; set; }
}

This configuration works fine when I'm mapping between an instantiation of one to the other, but a problem appears when I map between enumerables of these typ开发者_运维知识库es as in:

var dests = Mapper.Map<IEnumerable<Source>, Destination[]>(sources);

In this case, AutoMapper calls the Destination constructor for the first element to be mapped, but apparently proceeds to auto-map the remaining elements. The auto-mapping throws an exception because the identically-named CreatedDate properties are of different types.

If I change the name of one of the properties - say, Destination.Created - the constructor is called on all elements as you would expect.

I'm using the latest version of AutoMapper (v1.1.0.188). This seems like it must be a bug, but perhaps I've overlooked something?


You may want to try a different approach. I would try:

Mapper.CreateMap<Source, Destination>()
      .ForMember(d => d.CreatedDate,
                 opt => opt.MapFrom(s => s.CreatedDate.DateTime));

The mapping is slightly more verbose but it should get handled properly for each element. Not only that, but you can now remove the superfluous constructor on your Destination class.


It seemed unnatural to me for whatever reason, but apparently ConstructUsing only specifies how to construct the mapped object, and does not entail any particular mapping rules (or lack thereof) past the default. And since the default mapping copies values from like-named properties, my CreatedDate member was being copied automatically.

The solution is simply to supply the appropriate mapping rules in addition to the construction rule. In my case, since all my mapping is done during construction, it's simply a matter of omitting the mapping on the like-named property:

Mapper.CreateMap<Source, Destination>()
      .ConstructUsing(s => new Destination(s.CreatedDate.DateTime))
      .ForMember(d => d.CreatedDate, opt => opt.Ignore());


I happened upon this question when looking for the best way to automap from a DateTimeOffset to a DateTime. I don't know about earlier versions of AutoMapper, but in AutoMapper 3.2.1 I was able to create a map between DateTimeOffset and DateTime and AutoMapper will automatically use it any time it comes across the specified situation.

For example, the following code will convert a DateTimeOffset to DateTime using the DateTimeOffset.UtcDateTime property:

Mapper.CreateMap<DateTimeOffset, DateTime>().ConvertUsing(d => d.UtcDateTime);

Once the above map is registered in AutoMapper, it will convert using the given expression no matter what object is containing the values to convert between.

Hope this helps anyone coming across this situation using a newer version of AutoMapper.

0

精彩评论

暂无评论...
验证码 换一张
取 消