【问题标题】:Automapper - How to get containing class type when mapping a member?Automapper - 映射成员时如何获取包含类类型?
【发布时间】:2018-07-04 23:16:36
【问题描述】:

在运行时映射操作期间(例如,当您使用 ResolveUsing 或自定义 TypeConverter 时)是否可以获取源和目标成员的容器类(或至少类型)?

我知道当您将一个对象映射到另一个对象时,这些对象不必是某个“父”或“容器”对象的成员,但我说的是 AutoMapper 递归复制复杂对象的情况对象。

这是一个例子:

我在这里将“A 类”的汽车和船复制(或至少设置)到“B 类”。

public class VehicleCopyProfile : AutoMapper.Profile
{
    public VehicleCopyProfile()
    {
        this.CreateMap<CarA, CarB>();
        this.CreateMap<BoatA, BoatB>();

        this.CreateMap<WindshieldA, WindshieldB>(
            .ConvertUsing((s, d, resContext) =>
            {
                // *** How can I tell if s is coming from a Car or a Boat? ***
            });

    }
}

// Cars & Boats each have a Windshield

public class CarA
{
    public WindshieldA Windshield {get;set;}
}

public class BoatA
{
    public WindshieldA Windshield {get;set;}
}

public class WindshieldA
{
    public string Name {get;set;}
}





public class CarB
{
    public WindshieldB Windshield {get;set;}
}

public class BoatB
{
    public WindshieldB Windshield {get;set;}
}


public class WindshieldB
{
    public string Name {get;set;}
}

【问题讨论】:

  • 对不起,我不知道你在问什么。
  • 现在好点了吗? ...
  • 你不能。但是你可以通过 context.Items 传递根对象。

标签: c# .net automapper


【解决方案1】:

这是@Lucian Bargaoanu 在评论中提出的使用 AutoMapper ResolutionContext Items 的解决方案。这个想法是使用 Before 和 After Map 将信息存储在 Resolution Context 中。我使用 Stack 来跟踪整个关系链。

namespace SO51101306
{
    public static class IMappingExpressionExtensions
    {
        public static IMappingExpression<A, B> RegisterChainOfTypes<A, B>(this IMappingExpression<A, B> mapping)
        {
            mapping.BeforeMap((a, b, ctx) => {
                ctx.PushTypeInChainOfTypes(typeof(A));
            });

            mapping.AfterMap((a, b, ctx) => {
                ctx.PopLastTypeInChainOfTypes();
            });
            return mapping;
        }
    }

    public static class ResolutionContextExtensions
    {
        const string chainOfTypesKey = "ChainOfTypes";

        private static Stack<Type> GetOrCreateChainOfTypesStack(ResolutionContext ctx)
        {
            var hasKey = ctx.Items.ContainsKey(chainOfTypesKey);
            return hasKey ? (Stack<Type>)ctx.Items[chainOfTypesKey] : new Stack<Type>();
        }

        public static void PushTypeInChainOfTypes(this ResolutionContext ctx, Type type)
        {
            var stack = GetOrCreateChainOfTypesStack(ctx);
            stack.Push(type);
            ctx.Items[chainOfTypesKey] = stack;
        }

        public static Type PopLastTypeInChainOfTypes(this ResolutionContext ctx)
        {
            var stack = (Stack<Type>)ctx.Items[chainOfTypesKey];
            return stack.Pop();
        }

        public static bool HasParentType(this ResolutionContext ctx, Type parentType)
        {
            var stack = GetOrCreateChainOfTypesStack(ctx);
            return stack.Contains(parentType);
        }

    }

    public class CarCopyProfile : Profile
    {
        public CarCopyProfile()
        {
            CreateMap<CarA, CarB>().RegisterChainOfTypes();
            CreateMap<BoatA, BoatB>().RegisterChainOfTypes();

            CreateMap<WindshieldA, WindshieldB>()
            .ConvertUsing((wa,wb,ctx)=> {
                if(ctx.HasParentType(typeof(CarA)))
                {
                    Console.WriteLine("I'm coming from CarA");
                    //Do specific stuff here
                }
                else if (ctx.HasParentType(typeof(BoatA)))
                {
                    Console.WriteLine("I'm coming from boatA");
                    //Do specific stuff here
                }
                return wb;
            });

        }
    }

    public class CarA
    {
        public WindshieldA Windshield { get; set; }
    }

    public class BoatA
    {
        public WindshieldA Windshield { get; set; }
    }

    public class CarB
    {
        public WindshieldB Windshield { get; set; }
    }

    public class BoatB
    {
        public WindshieldB Windshield { get; set; }
    }

    public class WindshieldA
    {
        public string Name { get; set; }
    }

    public class WindshieldB
    {
        public string Name { get; set; }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Mapper.Initialize(c => c.AddProfile<CarCopyProfile>());

            var carA = new CarA{Windshield = new WindshieldA()};
            var boatA = new BoatA{Windshield = new WindshieldA()};

            var carB = Mapper.Map<CarB>(carA);
            var boatB = Mapper.Map<BoatB>(boatA);
        }
    }
}

这将输出:

I'm coming from CarA
I'm coming from boatA

【讨论】:

  • 除非其他人回复,否则我会在 2 天内将此标记为最佳答案。我认为“ResolutionContext.Items”和BeforeMap是这个问题的关键。但如果你想制定一个通用的解决方案,你需要做的还不止这些。你看,如果你的 Windshield 类本身有一个子类,这是行不通的。在我的最终解决方案中,对于每个 CreateMap,您将源类型添加到 CreateMap.BeforeMap 方法中的源类型链中。然后使用 AfterMap 从链中“弹出”该源类型。父类型将始终在链中排在第二位(集合除外,因为存在错误)。
  • 注意:After/BeforeMap 和 Profile.IncludeBase() 存在错误。如果您使用这些,那么在单个映射执行期间,将为每个包含的配置文件调用 After/BeforeMap。我认为 After/BeforeMap 应该具有多态行为,但它没有 - 至少对于 IncludeBase() - 也许它适用于 Include() (包括派生类型,而不是基本类型配置文件)。
  • @N73k 确定我的代码是示例代码,正如您所说,您必须处理对象之间的多级关系。
  • @N73k 我用 Stack 使它更通用,以便它可以处理多级关系
  • 感谢您的更新。更好的。我将其标记为答案。但是,仍然存在一些问题。 1.我认为您将“父母”与“祖先”混淆了。我只要求容器(父)类型。您可以将代码保留在那里,只需将名称“父”更改为“祖先”,然后添加静态方法以获取父类型。 2. 您的 HasParentType 方法也将包括当前类型。因此,如果从 Windshield 的 ConvertUsing 中,我说 HasParentType(typeof(WindshieldA)),结果将返回 true 而不是 false。无论如何,我会尝试稍后发布我的解决方案。
【解决方案2】:

另一种方法是使用自定义值解析器:

class CustomResolver<T1, T2>:IValueResolver ... { ... }

this.CreateMap<CarA, CarB>()
.ForMember(x => x.Windshield , opt => opt.ResolveUsing(new CustomResolver<CarA, CarB>()));

然后在你CustomResolver实现:

var windshieldB = Mapper.Map<WindshieldB>(windshieldA, x => {x.Items["type1"] = typeof(T1); x.Items["type2"] = typeof(T2);});

然后:

this.CreateMap<WindshieldA, WindshieldB>(
            .ConvertUsing((s, d, resContext) =>
            {
                // resContext.Options.Items["type1"]
            });

http://docs.automapper.org/en/stable/Custom-value-resolvers.html

【讨论】:

  • 感谢您的回答,但有很多问题:1. 这是硬编码到示例中的 2. 对于图形层次结构中的每个对象,您必须创建一个自定义 CustomResolver 类 3。每次调整对象层次结构(对象图)时,都必须更改所有这些代码的接线。 4. 通过使用 CustomResolver 来解决这个问题,您将不再使用 CustomResolver 来正常使用。 5.如果有人使用CustomResolver(正常使用),你已经希望能够知道,在CustomResolver中,容器类型是什么。 6. 需要重复的代码太多。
猜你喜欢
  • 1970-01-01
  • 2019-09-30
  • 2016-08-11
  • 2017-12-23
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多