我有很多类,它们具有类似的映射对象操作。abstract class
为了保持代码干燥,我想为所有这些类创建基础。baseabstract class
将具有如下通用映射函数:
public TEntity GetEntity(Result<TServiceClientEntity> res)
{
entity = MappingProfiles.TryMap(res.Value);
//some logic with result here
return entity;
}
Run Code Online (Sandbox Code Playgroud)
结果类:
public class Result<T>
{
public T Value{ get; set; }
//some more properties..
}
Run Code Online (Sandbox Code Playgroud)
但问题是我想不出如何映射通用类的方法:
public static class MappingProfiles
{
public static T2 TryMap<T,T2>(T t)
{
return (T2)Map((Real_T_type)t); //f.e.: the type is ExampleFrom
}
public static ExampleTo Map(ExampleFrom from)
{
return new ExampleTo
{
exapleValue = from.exapleValue
};
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我还希望TryMap
通用方法使用我预定义的Map
手动方法进行映射。
您可以使用反射 (C#)来完成以下任务:
public static TOut Map<TIn, TOut>(TIn source)
where TOut : new()
{
var inPropDict = typeof(TIn).GetProperties()
.Where(p => p.CanRead)
.ToDictionary(p => p.Name);
var outProps = typeof(TOut).GetProperties()
.Where(p => p.CanWrite);
var destination = new TOut();
foreach (var outProp in outProps) {
if (inPropDict.TryGetValue(outProp.Name, out var inProp)) {
object sourceValue = inProp.GetValue(source);
if (inProp.PropertyType != outProp.PropertyType) {
sourceValue = Convert.ChangeType(sourceValue, outProp.PropertyType);
}
outProp.SetValue(destination, sourceValue);
}
}
return destination;
}
Run Code Online (Sandbox Code Playgroud)
反射使您能够检查类型并获取其属性、字段等。
Type.GetProperties()
返回一个PropertyInfo
包含名称、类型和有关属性的其他信息的数组。它还允许您读取或写入对象的属性。
上面的代码只是一个快速而肮脏的示例,没有异常处理。它仅执行平面映射,不映射集合或嵌套对象。它还可以通过允许您声明不具有相同名称的属性的映射等来改进。
有一个工具可以完成所有这些事情,甚至更多,称为AutoMapper。
手动映射方法的解决方案
我建议定义一个这样的接口
public interface IMapper<T1, T2>
{
T2 Map(T1 input);
}
Run Code Online (Sandbox Code Playgroud)
具体实现示例:
public class ExampleFromToMapper : IMapper<ExampleFrom, ExampleTo>
{
public ExampleTo Map(ExampleFrom input)
{
return new ExampleTo {
ExampleValue = input.ExampleValue
};
}
}
Run Code Online (Sandbox Code Playgroud)
这个想法是使用依赖注入来完成选择正确映射器的工作。
您可以使用 NuGet 包Microsoft.Extensions.DependencyInjection作为示例。但还存在许多其他依赖注入框架。
编写一个配置映射器的方法(作为本例中的扩展方法):
public static IServiceCollection AddMappers(this IServiceCollection services)
{
return services
.AddSingleton<IMapper<ExampleFrom, ExampleTo>, ExampleFromToMapper>()
.AddSingleton<IMapper<OtherFrom, OtherTo>, OtherFromToMapper>();
}
Run Code Online (Sandbox Code Playgroud)
在某处定义容器:
public static class Config
{
public static ServiceProvider Container { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
并在应用程序启动时配置容器
var services = new ServiceCollection();
services
.AddMappers()
.AddTransient<MyForm>(); // See below
Config.Container = services.BuildServiceProvider();
Run Code Online (Sandbox Code Playgroud)
作为一个例子,让我们假设您有一个 WinForms 应用程序,其表单定义如下(它直接使用映射器,但它可以使用其他使用映射器的服务。DI 容器递归地解析依赖项并将它们注入到自动构造函数):
public partial class MyForm : Form
{
private readonly IMapper<ExampleFrom, ExampleTo> _mapper;
public MyForm(IMapper<ExampleFrom, ExampleTo> mapper)
{
_mapper = mapper;
InitializeComponent();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以像这样启动应用程序:
var frm = Config.Container.GetRequiredService<MyForm>();
Application.Run(frm);
Run Code Online (Sandbox Code Playgroud)
好吧,一开始看起来很复杂,但是一旦设置了基础知识,添加新服务就变得很容易。每个提供某些功能的类都被视为一项服务。
归档时间: |
|
查看次数: |
3068 次 |
最近记录: |