我的问题是,以最可维护的方式将一个对象映射到另一个对象的最佳方法是什么。我无法改变我们获得的Dto对象设置为更加规范化的方式,因此我需要创建一种方法将其映射到我们对象的实现。
以下示例代码显示了我需要发生的事情:
class Program
{
static void Main(string[] args)
{
var dto = new Dto();
dto.Items = new object[] { 1.00m, true, "Three" };
dto.ItemsNames = new[] { "One", "Two", "Three" };
var model = GetModel(dto);
Console.WriteLine("One: {0}", model.One);
Console.WriteLine("Two: {0}", model.Two);
Console.WriteLine("Three: {0}", model.Three);
Console.ReadLine();
}
private static Model GetModel(Dto dto)
{
var result = new Model();
result.One = Convert.ToDecimal(dto.Items[Array.IndexOf(dto.ItemsNames, "One")]);
result.Two = Convert.ToBoolean(dto.Items[Array.IndexOf(dto.ItemsNames, "Two")]);
result.Three = dto.Items[Array.IndexOf(dto.ItemsNames, "Three")].ToString();
return result;
}
}
class Dto
{
public object[] Items { get; set; }
public string[] ItemsNames { get; set; }
}
class Model
{
public decimal One { get; set; }
public bool Two { get; set; }
public string Three { get; set; }
}
我认为,如果我有某种mapper类可以接受模型对象propertyInfo,我要转换为的类型,以及我想要提取的“itemname”,那将是多么美妙的事情。有没有人有任何建议让这个更干净?
谢谢!
答案 0 :(得分:23)
我会选择AutoMapper,一个开源和自由映射库,它允许根据约定将一种类型映射到另一种类型(即映射具有相同名称和相同/派生/可转换类型的公共属性,与许多其他smart ones)。非常容易使用,可以让你实现这样的目标:
Model model = Mapper.Map<Model>(dto);
不确定您的具体要求,但AutoMapper还支持custom value resolvers,这可以帮助您编写特定映射器的单个通用实现。
答案 1 :(得分:5)
这是一个使用一点反射的可能的通用实现(伪代码,现在没有VS):
public class DtoMapper<DtoType>
{
Dictionary<string,PropertyInfo> properties;
public DtoMapper()
{
// Cache property infos
var t = typeof(DtoType);
properties = t.GetProperties().ToDictionary(p => p.Name, p => p);
}
public DtoType Map(Dto dto)
{
var instance = Activator.CreateInstance(typeOf(DtoType));
foreach(var p in properties)
{
p.SetProperty(
instance,
Convert.Type(
p.PropertyType,
dto.Items[Array.IndexOf(dto.ItemsNames, p.Name)]);
return instance;
}
}
用法:
var mapper = new DtoMapper<Model>();
var modelInstance = mapper.Map(dto);
创建映射器实例时这会很慢,但以后会快得多。
答案 2 :(得分:2)
/// <summary>
/// map properties
/// </summary>
/// <param name="sourceObj"></param>
/// <param name="targetObj"></param>
private void MapProp(object sourceObj, object targetObj)
{
Type T1 = sourceObj.GetType();
Type T2 = targetObj.GetType();
PropertyInfo[] sourceProprties = T1.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] targetProprties = T2.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var sourceProp in sourceProprties)
{
object osourceVal = sourceProp.GetValue(sourceObj, null);
int entIndex = Array.IndexOf(targetProprties, sourceProp);
if (entIndex >= 0)
{
var targetProp = targetProprties[entIndex];
targetProp.SetValue(targetObj, osourceVal);
}
}
}
答案 3 :(得分:1)
Efran Cobisi建议使用自动映射器。我已经使用Auto Mapper一段时间了,并且效果很好,直到找到更快得多的替代方法Mapster。
鉴于列表较大或IEnumerable,Mapster的性能优于Auto Mapper。我在某处发现了一个基准,该基准显示Mapster的运行速度是其6倍,但我找不到它。您可以查找它,然后在适合时使用Mapster。
答案 4 :(得分:0)
我根据 DKM 的回答创建了一个通用方法。
public static class DbHelper
{
public static T FillWith<T>(this T targetObj, T sourceObj)
{
Type T1 = sourceObj.GetType();
Type T2 = targetObj.GetType();
PropertyInfo[] sourceProprties = T1.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] targetProprties = T2.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var sourceProp in sourceProprties)
{
object osourceVal = sourceProp.GetValue(sourceObj, null);
int entIndex = Array.IndexOf(targetProprties, sourceProp);
if (entIndex >= 0)
{
var targetProp = targetProprties[entIndex];
targetProp.SetValue(targetObj, osourceVal);
}
}
return targetObj;
}
}
用法:
var oldUser = new User();
oldUser.FillWith(updatedUser);
答案 5 :(得分:0)
使用反射
public interface IModelBase
{
int Id { get; set; }
}
public interface IDtoBase
{
int Id { get; set; }
}
public class Client : IModelBase
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SomeType> ListOfSomeType { get; set; }
}
public class ClientDto : IDtoBase
{
public int Id { get; set; }
public string Name { get; set; }
}
public static class Extensions
{
public static TDto AsDto<T, TDto>(this T item)
where TDto : class, IDtoBase
where T : class, IModelBase
{
var list = item.GetType().GetProperties();
var inst = Activator.CreateInstance(typeof(TDto));
foreach (var i in list)
{
if (((TDto)inst).GetType().GetProperty(i.Name) == null)
continue;
var valor = i.GetValue(item, null);
((TDto)inst).GetType().GetProperty(i.Name).SetValue((TDto)inst, valor);
}
return (TDto)inst;
}
}
How to use it:
Client client = new { id = 1, Name = "Jay", ListOfSomeType = new List<SomeType>() };
ClientDto cdto = client.AsDto<Client, ClientDto>();