43

我的问题是,以最可维护的方式将一个对象映射到另一个对象的最佳方法是什么。我无法更改我们获得的 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; }
}

我认为如果我有某种映射器类可以接收模型对象 propertyInfo、我想要转换的类型以及我想要提取的“项目名称”,那将会很棒。有没有人有任何建议让这个更清洁?

谢谢!

4

7 回答 7

31

我会选择AutoMapper,这是一个开源和免费的映射库,它允许根据约定将一种类型映射到另一种类型(即映射具有相同名称和相同/派生/可转换类型的公共属性,以及许多其他智能类型)。非常容易使用,会让你实现这样的事情:

Model model = Mapper.Map<Model>(dto);

不确定您的具体要求,但 AutoMapper 还支持自定义值解析器,这应该可以帮助您编写特定映射器的单一通用实现。

于 2013-04-20T10:02:29.030 回答
9

这是一个可能的通用实现,使用一点反射(伪代码,现在没有 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);

创建映射器实例时这会很慢,但稍后会快得多。

于 2013-04-20T08:30:35.017 回答
6

Efran Cobisi 关于使用 Auto Mapper 的建议是一个很好的建议。我使用 Auto Mapper 已经有一段时间了,它运行良好,直到我找到了更快的替代方案Mapster

给定一个大列表或 IEnumerable,Mapster 的性能优于 Auto Mapper。我在某处找到了一个基准,显示 Mapster 的速度是原来的 6 倍,但我再也找不到它了。您可以查找它,然后,如果它适合您,请使用 Mapster。

于 2020-11-25T09:03:21.940 回答
1

映射两个对象的最快方法inline-mapping,但可能需要一些时间才能使用MappingGenerator

此外,您可以查看 Jason Bock 的基准进行比较,下面是更好的:

在此处输入图像描述 youtube 上的完整视频

于 2021-12-06T09:22:10.587 回答
0
/// <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);
       }
   }
}
于 2017-06-22T19:35:40.487 回答
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>();
于 2021-08-05T20:03:54.463 回答
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);
于 2021-04-08T13:11:01.417 回答