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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16118085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:04:30  来源:igfitidea点击:

Best Practices for mapping one object to another

c#design-patternsmapping

提问by Alex

My question is, what is the best way I can map one object to another in the most maintainable manner. I cannot change the way the Dto object that we are getting is setup to be more normalized so I need to create a way to map this to our implementation of their object.

我的问题是,以最可维护的方式将一个对象映射到另一个对象的最佳方式是什么。我无法改变我们获得的 Dto 对象的设置方式,使其更加规范化,因此我需要创建一种方法来将其映射到我们的对象实现。

Here is example code to show what I need to happen:

这是显示我需要发生的事情的示例代码:

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; }
}

I think what would be great is if I had some sort of mapper class that would take in the model objects propertyInfo, the type I want to convert to, and the "itemname" I want to pull out. Does anyone have any suggestions to make this cleaner?

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

Thanks!

谢谢!

回答by Stefano Altieri

This is a possible generic implementation using a bit of reflection (pseudo-code, don't have VS now):

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

Usage:

用法:

var mapper = new DtoMapper<Model>();
var modelInstance = mapper.Map(dto);

This will be slow when you create the mapper instance but much faster later.

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

回答by Efran Cobisi

I would opt for AutoMapper, an open source and free mapping library which allows to map one type into another, based on conventions (i.e. map public properties with the same names and same/derived/convertible types, along with many other smart ones). Very easy to use, will let you achieve something like this:

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

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

Not sure about your specific requirements, but AutoMapper also supports custom value resolvers, which should help you writing a single, generic implementation of your particular mapper.

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

回答by DKM

/// <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);
       }
   }
}