目前,我正在需要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创建新实例的场景。此外,仅当需求...
为Person
类为其自身创建映射将确保实例在映射时被深度复制: