2013-12-13 31 views
3

编辑:这原本是1:1映射,但我想通了,我需要一个更复杂的递归映射,这样一个新的问题被张贴在这里:How to recursively map entity to view model with Automapper function call?如何自动将.NET函数调用映射到属性?

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

实体例如:

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

视图模型例如:

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

理想的结果将是有一个地图功能,着眼于实体类的匹配方法返回类型,函数名称是“Get”+属性名称,然后执行它并将结果映射到视图模型。

public TTarget MapToViewModel<TSource, TTarget>(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<TTarget>(); 

    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调用的条件:

回答

2

虽然你也许能找到类似AutomapperValue Injecter可能已经包含了此功能,还有从编写一个小巧的实用方法阻止你什么只需使用Name,但您可以将它们扩展到任何你喜欢的。

要使用此功能,只需要调用它像这样:

var itemViewModel = MapToViewModel<Item, ItemViewModel>(item); 

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

+0

谢谢,我得到了这个工作。现在我有另一个困境。做这个递归有多难?例如,如果Product类有一个属性List 图像,我需要从Product类调用GetImages()? – user3092978

+0

我真的没有看到这将需要一个递归调用。你的意思是,如果方法和属性都存在于一个对象上,那么该方法需要调用?如果是这样的话,你可以切换属性和方法的if语句来首先检查方法。 – valverij

+0

不完全,我的意思是如果映射对象具有需要通过调用另一个函数来映射的另一个属性。你的回答帮助我确定我需要一些更复杂的东西,看起来像Automapper可能能够处理。我问另一个问题在这里http://stackoverflow.com/questions/20573600/how-to-recursively-map-entity-to-view-model-with-automapper-function-call – user3092978

1

使用ServiceStack进行映射将非常难以制定一个通用解决方案,因为您基本上必须自己编写代码以将Products与匹配。

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

// one time config 
Mapper.CreateMap<Item, ItemViewModel>(); 
Mapper.AssertConfigurationIsValid(); 

// whenever you map 
Item item = // whatever 
ItemViewModel viewModel = Mapper.Map<ItemViewModel>(item); 
// viewModel.Products is populated from item.GetProducts() 

手动改变特定事物的映射方式也很容易,例如,你可以手动设置这个使用这个CreateMap

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

它看起来像AutoMapper是一个更强大的映射。即使您对其他部分使用ServiceStack,为您的映射使用AutoMapper可能也是值得的。

+0

不错,我想AutoMapper可以做这样的事情(即使它只是使用'.AfterMap'功能) – valverij

+0

不错,我没有考虑Automapper。 Automapper是否会递归地支持这个? – user3092978

+0

是的(我认为;不确定你的意思是“递归”),例如如果'Products'和'GetProducts'返回不同的类型,但在这两种产品类型之间创建了一个映射(例如'Mapper.CreateMap ();'),它也会自动映射这些项目。 –

相关问题