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

时间:2013-12-13 16:21:13

标签: c# asp.net servicestack

编辑:这最初用于1:1映射,但我发现我需要更复杂的递归映射,因此在此处发布了一个新问题:How to recursively map entity to view model with Automapper function call?

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

实体示例:

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

ViewModel示例:

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

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

2 个答案:

答案 0 :(得分:2)

虽然您可能会发现像AutomapperValue Injecter这样的内容已经包含此功能,但是没有什么可以阻止您编写快速的小实用程序方法:

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次调用中的条件只使用Name,但您可以将它们扩展为您喜欢的任何内容。

要使用此功能,请按以下方式调用:

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

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

答案 1 :(得分:1)

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

您可以使用其他映射库,例如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进行映射。