【问题标题】:Pass Objects to AutoMapper Mapping将对象传递给 AutoMapper 映射
【发布时间】:2015-10-23 06:43:50
【问题描述】:

我正在使用 AutoMapper,被映射到的实体的一些值是我当前方法中的变量。我已经尝试谷歌它但无济于事。我可以将一组 KeyValue Pairs 或一个对象或其他东西传递给我的映射以让它使用这些值吗?

后期映射修改示例

//comment variable is a Comment class instance
var imageComment = AutoMapper.Mapper.Map<Data.ImageComment>(comment);
//I want to pass in imageId so I dont have to manually add it after the mapping
imageComment.ImageId = imageId;

【问题讨论】:

    标签: c# asp.net-mvc-4 automapper


    【解决方案1】:

    AutoMapper 9.0.0

    从 8.0.0 版开始,AutoMapper 的 API 已更改。在这样做时,ResolveUsingMapFrom 合并。更多信息请查看相应的pull request

    个人资料

    public class CoreProfile : Profile
    {
        public CoreProfile()
        {
            CreateMap<Source, Destination>()
                .ForMember(d => d.Bar,
                    opt => opt.MapFrom(
                        (src, dst, _, context) => context.Options.Items["bar"]
                    )
                );    
        }
    }
    

    映射

    var destination = mapper.Map<Destination>(
        source,opt => {
            opt.Items["bar"] = "baz";
        }
    );
    

    【讨论】:

    • 如果您写几句话来说明与上一个答案(Automapper 6.0.2)相比发生了什么变化,那就太好了。
    • @Razor23Donetsk,感谢您的提示。我已经添加了版本之间更改的缺失描述。
    • @Razor23Donetsk 缺少的描述是 MapFrom() 操作(和其他几个)有新的重载,将解析上下文暴露给映射表达式。
    【解决方案2】:

    可以使用Items Dictionary 选项将对象传递给解析器。执行此操作的标准 API 非常冗长(如已接受的答案所示),但可以使用一些扩展方法很好地简化:

    /// <summary>
    /// Map using a resolve function that is passed the Items dictionary from mapping context
    /// </summary>
    public static void ResolveWithContext<TSource, TDest, TMember, TResult>(
        this IMemberConfigurationExpression<TSource, TDest, TMember> memberOptions, 
        Func<TSource, IDictionary<string, object>, TDest, TMember, TResult> resolver
    ) {
        memberOptions.ResolveUsing((src, dst, member, context) => resolver.Invoke(src, context.Items, dst, member));
    }
    
    public static TDest MapWithContext<TSource, TDest>(this IMapper mapper, TSource source, IDictionary<string, object> context, Action<IMappingOperationOptions<TSource, TDest>> optAction = null) {
        return mapper.Map<TSource, TDest>(source, opts => {
            foreach(var kv in context) opts.Items.Add(kv);
            optAction?.Invoke(opts);
        });
    }
    

    可以这样使用:

    // Define mapping configuration
    Mapper.CreateMap<Comment, ImageComment>()
        .ForMember(
            d => d.ImageId,
            opt => opt.ResolveWithContext(src, items, dst, member) => items["ImageId"])
        );
    
    // Execute mapping
    var context = new Dictionary<string, object> { { "ImageId", ImageId } };
    return mapper.MapWithContext<TSource, TDest>(source, context);
    

    如果您有一个通常需要传递给映射器解析器的对象(例如,当前用户),您可以更进一步,定义更专业的扩展:

    public static readonly string USER_CONTEXT_KEY = "USER";
    
    /// <summary>
    /// Map using a resolve function that is passed a user from the
    /// Items dictionary in the mapping context
    /// </summary>
    public static void ResolveWithUser<TSource, TDest, TMember, TResult>(
        this IMemberConfigurationExpression<TSource, TDest, TMember> memberOptions,
        Func<TSource, User, TResult> resolver
    ) {
        memberOptions.ResolveWithContext((src, items, dst, member) =>
            resolver.Invoke(src, items[USER_CONTEXT_KEY] as User));
    }
    
    /// <summary>
    /// Execute a mapping from the source object to a new destination
    /// object, with the provided user in the context.
    /// </summary>
    public static TDest MapForUser<TSource, TDest>(
        this IMapper mapper,
        TSource source,
        User user,
        Action<IMappingOperationOptions<TSource, TDest>> optAction = null
    ) {
        var context = new Dictionary<string, object> { { USER_CONTEXT_KEY, user } };
        return mapper.MapWithContext(source, context, optAction);
    }
    

    可以这样使用:

    // Create mapping configuration
    Mapper.CreateMap<Source, Dest>()
        .ForMember(d => d.Foo, opt => opt.ResolveWithUser((src, user) src.Foo(user));
    
    // Execute mapping
    return mapper.MapWithUser(source, user);
    

    【讨论】:

    【解决方案3】:

    对于 Automapper 6.0.2:

    简介:

    public class CoreProfile : Profile
    {
        public CoreProfile()
        {
            CreateMap<Source, Dest>()
                .ForMember(d => d.Foo,
                    opt => opt.ResolveUsing(
                        (src, dst, arg3, context) => context.Options.Items["Foo"]
                    )
                );
        }
    }
    

    映射:

        var result = Mapper.Map<PlanResult>(aa, opt => {
            opt.Items["Foo"] = 2;
            opt.Items["OtherFoo"] = 1000;
        });
    

    【讨论】:

      【解决方案4】:

      AutoMapper 开箱即用地处理这种键值对场景。

      Mapper.CreateMap<Source, Dest>()
          .ForMember(d => d.Foo, opt => opt.ResolveUsing(res => res.Context.Options.Items["Foo"]));
      

      然后在运行时:

      Mapper.Map<Source, Dest>(src, opt => opt.Items["Foo"] = "Bar");
      

      深入研究上下文项目有点冗长,但你去吧。

      【讨论】:

      • ResolveUsing 和 ConstructUsing 方法一起使用有什么方法吗?
      • ResolveUsing 方法是否可能不再接受仅将上下文作为参数的 lambda,或者我是否缺少扩展函数?我必须先定义一个以 (input, output, memberValue, context) 为参数的 lambda,然后才能让它与 Automapper 5.2 一起使用。当然,它仍然可能是编译器无法确定它需要使用哪个重载......
      • 5.x 添加了重载,并且调整了这些重载,这样您就不会遇到奇怪的编译时错误。
      • res.Context 出现错误 - 源模型不包含上下文定义。使用 .net core 2.0 和 AutoMapper 6.2.1
      【解决方案5】:

      假设你有这两个对象:

      public class ObjectA {
       public string Property1 { get; set; }
       public int Property2 { get; set; }
      }
      public class ObjectB {
       public string Property1 { get; set; }
       public int Property2 { get; set; }
      }
      

      如果您想将ObjectA 类型的现有对象复制到ObjectB 类型的新对象中,您必须使用 AutoMapper 执行此操作:

      var objectA = new ObjectA { Property1 = "Hello, World!", Property2 = 1 }
      var objectB = new ObjectB();
      
      // Copy data from a to b
      AutoMapper.Mapper
        .CreateMap<ObjectA, ObjectB>()
        .BeforeMap((source, dest) => { dest.ImageId = imageId });
      AutoMapper.Mapper.Map(objectA, objectB); // source, destination;
      
      // Or:
      var objectB = AutoMapper.Mapper.Map<ObjectB>(objectA);
      

      【讨论】:

      • 是的,我有这部分工作。我基本上在ObjectB 上有一个附加属性,它不在ObjectA 上,但它是我的控制器中的一个变量,我想在运行时传递给映射。
      • 您是否尝试过使用反射?你能发布示例代码吗?
      • 我添加了“BeforeMap”示例,你可以用它来管理映射。
      • 所以我基本上重新定义了我要在哪里使用它的地图?抱歉,通常不是 C# 人,所以还在搞清楚
      • 这就像在映射后手动添加一样冗长
      猜你喜欢
      • 1970-01-01
      • 2011-07-17
      • 1970-01-01
      • 1970-01-01
      • 2017-07-20
      • 1970-01-01
      • 2013-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多