Automapper-确保子属性与源实例不同,并使用IOC创建

问题描述 投票:0回答:1

目前,我正在需要Automapper使用IOC创建新实例的场景。另外,仅当需求增加时,我才需要为Automapper注册映射(因此,我不使用配置文件)。为了达到相同的目的,我做了以下工作。

var cfg = new MapperConfigurationExpression();
cfg.ConstructServicesUsing(type =>
            {
                return CreateInstanceUsingIOC(type);
            });

cfg.CreateMap<Model1, Model2>()
    .ConstructUsingServiceLocator();
var config = new MapperConfiguration(cfg);
var mapper = config.CreateMapper();

var model1UsingIoC = CreateModel1UsingIoC();
model1UsingIoC.MyProfile = new Person();
model1UsingIoC.MyProfile.FirstName = "New First Name";
model1UsingIoC.MyProfile.LastName = "New Last Name";
model1UsingIoC.CommonProperty = "This wont be copied";
var model2b = mapper.Map<Model2>(model1UsingIoC);

这可以按需工作,但是内部属性Model1.MyProfile存在问题。 Source和Destination中MyProfile的实例看起来是相同的。

ReferenceEquals(model2b.MyProfile,model1UsingIoC.MyProfile) // True

此外,我想使用IoC创建每个用户定义的子属性。为此,我添加了修改后的ConstructServicesUsing语句为

 cfg.ConstructServicesUsing(type =>
            {
                var instance = CreateInstance(type);
                return ReassignProperties(instance);
            });

其中ReassignProperties定义为

public static object ReassignProperties(object obj)
{
    if (obj == null) return null;
    Type objType = obj.GetType();

    PropertyInfo[] properties = obj.GetType().GetProperties();
    foreach (PropertyInfo property in properties)
    {
        // Check if its a collection
        if (property.IsEnumerable())
        {
            if (property.GetValue(obj, null) is IEnumerable elems)
            {
                foreach (var item in elems)
                {
                    ReassignProperties(item);
                }
            }
        }
        else
        {
            // If it is User Defined Type, Recursively loop again
            if (property.PropertyType.Assembly == objType.Assembly)
            {
                var newValue = CreateInstance(property.PropertyType);
                property.SetValue(obj, newValue);
                var value = property.GetValue(obj);
                ReassignProperties(value);
            }
            else
            {
                var propValue = property.GetValue(obj, null);
                Console.WriteLine($"{property.Name} = {propValue}");

            }

        }
    }
    return obj;
}

但是,正如人们可能会想到的那样,这也无济于事,并且在映射时,同一实例被复制为子属性。有人可以指导我如何确保在使用Automapper时如何为用户定义的子属性创建一个新实例(即,创建新实例并复制值,除非该类型是原始类型)?

更新001

根据Lucian的建议,我尝试如下创建自定义映射器,并将其添加到Mapping Registery。

public class NewInstanceMapper : IObjectMapper
{
    public bool IsMatch(TypePair context) => true;


    public static object CreateInstance(Type type)
    {
        try
        {
            var methodInfo = typeof(IoC).GetMethod(nameof(IoC.Get), BindingFlags.Public | BindingFlags.Static);
            var genericMethodInfo = methodInfo.MakeGenericMethod(type);
            return genericMethodInfo.Invoke(null, new object[] { null });
        }
        catch (Exception)
        {
            return Activator.CreateInstance(type);
        }
    }

    public Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
    {
        return Expression.Call(this.GetType(), nameof(CreateInstance), null, Expression.Constant(destExpression.Type));
    }
}

并将其添加到MappingRegistery中使用>

var cfg = new MapperConfigurationExpression();
cfg.Mappers.Insert(0,new NewInstanceMapper());

但是,正如人们可能猜到的那样,这创建了一个新实例,但没有复制该值

目前,我正在需要Automapper使用IOC创建新实例的场景。此外,仅当需求...

c# automapper unity-container caliburn.micro
1个回答
0
投票

Person类为其自身创建映射将确保实例在映射时被深度复制:

© www.soinside.com 2019 - 2024. All rights reserved.