问题
使用自动映射器平展大型多级对象,以允许我使用Dapper将值传递给sql。
选项
在阅读了文档以及关于SO的各种答案之后,似乎有两个选择,使用ForMember
或使用NamingConventions。这些对象都不适合我,因为对象太大,ForMember
几乎需要手动映射。 NamingConventions表示使用目标对象的位置的一些不可读且不相关的名称。
refs:ForMember flattening
我确实找到了一个使用ITypeConverter
(Custom type converters)的非常基本的示例,并尝试使其对我有用,但是经过三天的尝试,我真的很努力我应该寻求帮助。
在搜索了各种论坛之后,我决定尝试创建一个通用的解决方案,因为我将在整个项目中多次重复使用此解决方案。
请注意,如果有更好的方法(我想念现有的扩展名或配置,我很想听听。如果不能,请提供一些有关如何完成任务的指导-我感觉就像我的头撞在砖墙上)
在下面,我添加了我创建的控制台应用程序以尝试解决此问题(我觉得我应该说我知道这段代码很糟糕,我只是在hacking以期希望能起作用)
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
namespace AutomapperTests
{
public class Program
{
static void Main(string[] args)
{
var property = Program.CreateProperty();
var config = new MapperConfiguration(cfg => cfg.CreateMap<PropertyDto, FlatProperty>()
.ConvertUsing<MyTypeConverter<PropertyDto, FlatProperty>>());
var mapper = config.CreateMapper();
var flatProperty = mapper.Map<FlatProperty>(property);
}
private static PropertyDto CreateProperty()
{
var property = new PropertyDto();
property.Guid = new Guid();//not mapping to guid
property.SomeTestVal = 123456;
property.TransactionStatus = TransactionStatus.ForSale;
property.Address = new AddressDto();
property.Detail = new DetailDto();
property.Address.PostCode1 = "ng10";
property.Detail.LandArea = 12;
return property;
}
}
public class MyTypeConverter<T, TK> : ITypeConverter<T, TK>
{
public TK Convert(T source, TK destination, ResolutionContext context)
{
var sourceType = source.GetType();
PropertyInfo[] sourceClassProperties = sourceType.GetProperties();//note: i've tried flattening the structure at this point but failed
var properties = GetCustomObjectsFromProperties(sourceType.GetProperties());
destination = (TK)Activator.CreateInstance(typeof(TK));
var destinationType = destination.GetType();
PropertyInfo[] destinationProperties = destinationType.GetProperties();
foreach (PropertyInfo sourceClassProperty in sourceClassProperties)
{
SetValue(sourceClassProperty, source, destinationProperties, destination);
}
return destination;
}
private void SetValue(PropertyInfo sourceClassProperty, T source, PropertyInfo[] destinationProperties, TK destination)
{
bool isNativeClass = IsNativeClass(sourceClassProperty);
object sourceValue = sourceClassProperty.GetValue(source);
string sourceName = sourceClassProperty.Name;
PropertyInfo destProperty = destinationProperties.FirstOrDefault(x => x.Name == sourceName);
if (destProperty == null && !isNativeClass)
{
//note: my idea was to use recursion to enter the object if necessary and search for a matching value ---maybe i'm really overthinking this???
}
SetDestinationValue(destProperty, destination, sourceValue);
}
private bool IsNativeClass(PropertyInfo sourceClassProperty)
{
return sourceClassProperty.GetType().Namespace == "System";
}
private void SetDestinationValue(PropertyInfo destProperty, TK destination, object sourceValue)
{
if (destProperty != null)
{
destProperty.SetValue(destination, sourceValue);
}
}
private PropertyInfo[] GetCustomObjectsFromProperties(PropertyInfo[] propertyInfo)
{
return propertyInfo.Where(info => info.Module.Name == GetAssemblyName()).ToArray();
}
private string GetAssemblyName()
{
return $"{typeof(T).Assembly.GetName().Name}.dll";
}
}
public class FlatProperty
{
public Guid Guid { get; set; }
public int SomeTestVal { get; set; }
public int? TransactionStatusId { get; set; }
public string Postcode1 { get; set; }
public decimal? LandArea { get; set; }
}
public class PropertyDto
{
public Guid Guid { get; set; }
public int SomeTestVal { get; set; }
public TransactionStatus? TransactionStatus { get; set; }
public AddressDto Address { get; set; }
public DetailDto Detail { get; set; }
}
public class AddressDto
{
public string PostCode1 { get; set; }
}
public class DetailDto
{
public decimal? LandArea { get; set; }
}
public enum TransactionStatus
{
Sold = 1,
ForSale = 2
}
}
编辑:
我刚刚发现我似乎可以做这样的事情:
在配置中:
CreateMap<AddressDto, CreatePropertyDataModel>();
CreateMap<DetailDto, CreatePropertyDataModel>();
CreateMap<PropertyDto, CreatePropertyDataModel>()
.ForMember(dest => dest.Guid,
opts => opts.MapFrom(
src => src.Guid.ToString()));
设置对象:
var property = new PropertyDto();
property.Guid = new Guid();//not mapping to guid
property.SomeTestVal = 123456;
property.TransactionStatus = TransactionStatus.ForSale;
property.Address = new AddressDto();
property.Detail = new DetailDto();
property.Address.PostCode1 = "ng10";
property.Detail.LandArea = 12;
return property;
在某些班上:
var dataModel = new UpsertPropertyDataModel();
_mapper.Map(_property, dataModel);
_mapper.Map(_property.Address, dataModel);
_mapper.Map(_property.Detail, dataModel);
我的问题是,我得到了错误Unmapped members were found
,这些未映射的成员引用了地址或明细无法映射到的成员,因此我必须将那些成员设置为Ignore()
,是这是我使用不正确的可行解决方案? (请注意,我现实世界中的物体要大得多)