将一个对象映射到另一个对象的最佳实践

Ale*_*lex 33 c# mapping design-patterns

我的问题是,以最可维护的方式将一个对象映射到另一个对象的最佳方法是什么.我无法改变我们获得的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; }
}
Run Code Online (Sandbox Code Playgroud)

我认为,如果我有某种mapper类可以接受模型对象propertyInfo,我要转换为的类型,以及我要提取的"itemname",那会是多么美妙.有没有人有任何建议让这个更干净?

谢谢!

Efr*_*isi 23

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

Model model = Mapper.Map<Model>(dto);
Run Code Online (Sandbox Code Playgroud)

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

  • 我们之前使用过自动播放器,但由于它的性能很慢,我们有理由放弃它. (12认同)
  • 同意.做了同样的事情,结果是更多的工作放弃了自动播放器,然后首先编写一个自定义的.Automapper的性能非常低 (5认同)
  • 我们在性能上遇到了同样的问题,不再使用它. (3认同)
  • 尝试 [Mapster](https://github.com/MapsterMapper/Mapster) 来提高性能。YMMV,但它说它要快得多。 (3认同)

Ale*_*lex 9

这个问题很老了,而且事情已经发生了变化。现代 C# 和 .NET 6 具有一个称为“源生成器”的强大功能 - 它在编译时根据对象的元数据生成 C# 代码。

您实际上会得到如下自动生成的代码:

//auto-generated code at build time
objA.Property1 = objB.Property1;
objA.Property2 = objB.Property2;
Run Code Online (Sandbox Code Playgroud)

这就像“提前”反射,但它在构建时工作,这使得它快如闪电。

因此,2023 年映射对象的最有效方法是使用源生成器。您可以编写自己的包或使用像Riok.Mapperly https://github.com/riok/mapperly这样的包(我不隶属,一段时间以来一直是一个快乐的用户)


Ste*_*eri 6

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

用法:

var mapper = new DtoMapper<Model>();
var modelInstance = mapper.Map(dto);
Run Code Online (Sandbox Code Playgroud)

创建映射器实例时这会很慢,但后来要快得多.


Amj*_*ous 6

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

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