如何自动将.NET函数调用映射到属性?

编辑:这最初用于1:1映射,但我发现我需要一个更复杂的递归映射,所以在这里发布了一个新问题: 如何使用Automapper函数调用递归地将实体映射到视图模型?

我正在尝试使用ServiceStack ConvertTo 方法将实体类函数一般映射到视图模型。 这映射所有相似的类型和属性名称并且工作正常,但我想找到一种方法将函数的结果映射到属性。 这是一些代码

实体示例:

public class Item { public long Id {get; set;} public List GetProducts() { return Repository.GetAll(); } } 

ViewModel示例:

  public class ItemViewModel { public long Id {get;set;} public List Products {get; set;} } 

理想的结果是使用map函数查看实体类以查找与返回类型匹配的方法,函数名称为“Get”+ Property name,然后执行它并将结果映射到视图模型。

虽然您可能会发现像Automapper或Value Injecter这样的东西已经包含了这个function,但是没有什么可以阻止您编写一个快速的小实用程序方法:

 public TTarget MapToViewModel(TSource source) where TTarget : new() { var sourceType = source.GetType(); var targetProperties = typeof(TTarget).GetProperties(BindingFlags.Public | BindingFlags.Instance); var sourceProperties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance); var sourceMethods = sourceType.GetMethods(BindingFlags.Public | BindingFlags.Instance); var target = Activator.CreateInstance(); foreach (var prop in targetProperties) { var sourceProp = sourceProperties.FirstOrDefault(x => x.Name == prop.Name); if (sourceProp != null) { prop.SetValue(target, sourceProp.GetValue(source, null), null); } else { var sourceMethod = sourceMethods.FirstOrDefault(x => x.Name == "Get" + prop.Name); if (sourceMethod != null) { prop.SetValue(target, sourceMethod.Invoke(source, null), null); } } } return target; } 

上面的.FirstOrDefault调用中的条件只使用Name ,但您可以将它们扩展为您喜欢的任何内容。

要使用它,只需调用它:

 var itemViewModel = MapToViewModel(item); 

如果常规模型中的方法有任何参数,请务必小心。 如果您发现模型太复杂(或者不遵循一个或两个命名约定),那么使用AutoMapper之类的东西可能会更好。 但是对于简单的映射,这样的事情应该可以完成。

使用ServiceStack进行此映射很难做出一般解决方案,因为您基本上必须编写代码以自行匹配ProductsGetProducts

您可以使用另一个映射库,如AutoMapper ,它会自动使用GetX()来填充X

 // one time config Mapper.CreateMap(); Mapper.AssertConfigurationIsValid(); // whenever you map Item item = // whatever ItemViewModel viewModel = Mapper.Map(item); // viewModel.Products is populated from item.GetProducts() 

手动更改某些事物的映射方式也更容易,例如,您可以使用此CreateMap手动设置此项:

 Mapper.CreateMap() .ForMember(dest => dest.Products, opt => opt.ResolveUsing(src => src.GetProducts())); 

看起来AutoMapper是一个更强大的映射器。 即使您将ServiceStack用于其他部分,使用AutoMapper进行映射也许是值得的。