Проблема
Сведение большого многоуровневого объекта с помощью автоматического сопоставления, чтобы я мог передавать значения в sql с помощью Dapper.
Параметры
После прочтения документов и различных ответовна SO, кажется, есть два варианта, используя ForMember
или используя NamingConventions.Ни один из них не может работать для меня, так как объект настолько велик, что ForMember
потребует почти столько же ручного отображения.NamingConventions будет означать некоторые нечитаемые и нерелевантные имена, для которых используется целевой объект.
ссылки, которые я использовал: ForMember уплощение
Я нашел действительно простой пример использования ITypeConverter
( пользовательских преобразователей типов) и пытался заставить его работать на меня, но я действительно борюсь, после трех дней попыток я подумал, что должен обратиться за помощью.
После поиска на различных форумах я решил попробовать исоздайте универсальное решение, так как я буду многократно использовать его на протяжении всего проекта.
Обратите внимание, если есть лучший способ (существующее расширение или конфигурация, которую я пропустил, я хотел бы услышатьЕсли нет, не могли бы вы дать несколько советов о том, как выполнить мою задачу - мне кажется, что я бьюсь головой о кирпичную стену)
ниже Я добавил консольное приложение, которое я создал, чтобы попытатьсярешить эту проблему (я чувствую, что должен сказать, что я знаю, что этот код ужасен, я только что взломал его в надежде, что что-то сработает)
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()
, это жизнеспособное решение, которое я использую неправильно?(обратите внимание, мои объекты в реальном мире намного больше)