Automapper:将参数传递给Map方法

dav*_*ooh 30 .net c# automapper-3

我在项目中使用Automapper,我需要动态定值目标对象的字段.

在我的配置中,我有类似的东西:

cfg.CreateMap<Message, MessageDto>()
    // ...
    .ForMember(dest => dest.Timestamp, opt => opt.MapFrom(src => src.SentTime.AddMinutes(someValue)))
    //...
    ;
Run Code Online (Sandbox Code Playgroud)

someValue配置中的代码是,我需要在运行时传递给映射器并且不是所述源对象的一个字段的参数.

有没有办法实现这个目标?像这样的东西:

Mapper.Map<MessageDto>(msg, someValue));
Run Code Online (Sandbox Code Playgroud)

Ric*_*ard 50

您不能完全按照自己的意愿行事,但是当您调用Map时,可以通过指定映射选项来获得非常接近的结果.忽略配置中的属性:

cfg.CreateMap<Message, MessageDto>()
    .ForMember(dest => dest.Timestamp, opt => opt.Ignore())
    ;
Run Code Online (Sandbox Code Playgroud)

然后在调用地图时传入选项:

int someValue = 5;
var dto = Mapper.Map<Message, MessageDto>(message, opt => 
    opt.AfterMap((src, dest) => dest.TimeStamp = src.SendTime.AddMinutes(someValue)));
Run Code Online (Sandbox Code Playgroud)

请注意,您需要使用Mapper.Map<TSrc, TDest>重载来使用此语法.

  • 在这种情况下不是更容易而不是AfterMap只分配dto.TimeStamp = message.SendTime.AddMinutes(someValue); (8认同)
  • @Window 这个解决方案不会每次都会发生,除非您在执行“CreateMap”时在配置文件中定义“AfterMap”,但这里的情况并非如此。 (2认同)

小智 15

您绝对可以使用自定义实现完全执行您想要的操作ITypeConverter<TSource, TDestination>

  1. 调用时Map,您可以使用第二个回调参数使用自定义参数配置转换上下文。
  2. Convert客户类型转换器的方法中,您可以从作为第三个参数传递的上下文中恢复参数。

完整的解决方案:

namespace BegToDiffer
{
    using AutoMapper;
    using System;

    /// <summary>
    /// "Destiantion" type.
    /// </summary>
    public class MessageDto
    {
        public DateTime SentTime { get; set; }
    }

    /// <summary>
    /// "Source" type.
    /// </summary>
    public class Message
    {
        public DateTime Timestamp { get; set; }
    }

    /// <summary>
    /// Extension methods to make things very explicit.
    /// </summary>
    static class MessageConversionExtensions
    {
        // Key used to acccess time offset parameter within context.
        static readonly string TimeOffsetContextKey = "TimeOffset";

        /// <summary>
        /// Recovers the custom time offset parameter from the conversion context.
        /// </summary>
        /// <param name="context">conversion context</param>
        /// <returns>Time offset</returns>
        public static TimeSpan GetTimeOffset(this ResolutionContext context)
        {
            if (context.Items.TryGetValue(TimeOffsetContextKey, out var timeOffset))
            {
                return (TimeSpan)timeOffset;
            }

            throw new InvalidOperationException("Time offset not set.");
        }

        /// <summary>
        /// Configures the conversion context with a time offset parameter.
        /// </summary>
        /// <param name="options"></param>
        /// <param name="timeOffset"></param>
        public static IMappingOperationOptions SetTimeOffset(this IMappingOperationOptions options, TimeSpan timeOffset)
        {
            options.Items[TimeOffsetContextKey] = timeOffset;
            // return options to support fluent chaining.
            return options; 
        }
    }

    /// <summary>
    /// Custom type converter.
    /// </summary>
    class MessageConverter : ITypeConverter<Message, MessageDto>
    {
        public MessageDto Convert(Message source, MessageDto destination, ResolutionContext context)
        {
            if (destination == null)
            {
                destination = new MessageDto();
            }

            destination.SentTime = source.Timestamp.Add(context.GetTimeOffset());

            return destination;
        }
    }

    public class Program
    {
        public static void Main()
        {
            // Create a mapper configured with our custom type converter.
            var mapper = new MapperConfiguration(cfg =>
                cfg.CreateMap<Message, MessageDto>().ConvertUsing(new MessageConverter()))
                    .CreateMapper();

            // Setup example usage to reflect original question.
            int someValue = 5;
            var msg = new Message { Timestamp = DateTime.Now };

            // Map using custom time offset parameter.
            var dto = mapper.Map<MessageDto>(msg, options => options.SetTimeOffset(TimeSpan.FromMinutes(someValue)));

            // The proof is in the pudding:
            Console.WriteLine("msg.Timestamp = {0}, dto.SentTime = {1}", msg.Timestamp, dto.SentTime);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 8

使用Map方法时另一个可能的选项是使用Items字典.例:

int someValue = 5;
var dto = Mapper.Map<Message>(message, 
    opts => opts.Items["Timestamp"] = message.SentTime.AddMinutes(someValue));
Run Code Online (Sandbox Code Playgroud)

它的代码少一点,并且具有动态指定字段的优点.