自动映射器-映射成员时如何获取包含类的类型?

时间:2018-06-26 19:56:19

标签: c# .net automapper

在运行时映射操作期间(例如,当您使用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;}
}

2 个答案:

答案 0 :(得分:1)

这是@Lucian Bargaoanu在评论中建议的使用AutoMapper ResolutionContext项目的解决方案。想法是使用“前后映射”在“解析上下文”中存储信息。我使用堆栈来跟踪整个关系链。

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

答案 1 :(得分:-1)

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

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