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还支持自定义值解析器,这可以帮助您编写特定映射器的单个通用实现.
这个问题很老了,而且事情已经发生了变化。现代 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这样的包(我不隶属,一段时间以来一直是一个快乐的用户)
这是一个使用一点反射的可能的通用实现(伪代码,现在没有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)
创建映射器实例时这会很慢,但后来要快得多.