自动映射条件回退

时间:2016-12-01 13:32:57

标签: c# rest asp.net-web-api automapper

上下文:使用AutoMapper 5.1.1的RESTful ASP.NET Core Web API。

我正在尝试实现一种通用方法来在客户端请求时扩展引用的资源。 E.g:

GET /api/Products/4711返回

{
  "id":"4711",
  "productName":"flux capacitor",
  "category": {
    "id": "42",
    "obj": null,
  }
}

GET /api/Products/4711?expand=category应该返回

{
  "id":"4711",
  "productName":"flux capacitor",
  "category": {
    "id": "42",
    "obj": {
      id:"42",
      name: "time travelling",
      isRestricted: true,
    },
  }
}

到目前为止,我设法通过

来达到目的

1。在我的资源中创建用作属性的类:

public class ExpandableReference<TAggregateRoot,TResource>
{
    public int Id { get; set; }
    public TResource Obj { get; set; }
}

2。这个ITypeConverter的实现(请注意我的通用存储库的使用,这是成功注入的):

public class ReferenceExpander<TAggregateRoot, TResource> : ITypeConverter<int, ExpandableReference<TAggregateRoot, TResource>>
    where TAggregateRoot : AggregateRoot
{
    private readonly IRepository<TAggregateRoot> repository;

    public ReferenceExpander(IRepository<TAggregateRoot> repository)
    {
        this.repository = repository;
    }

    public ExpandableReference<TAggregateRoot, TResource> Convert(
        int source, 
        ExpandableReference<TAggregateRoot, TResource> destination, 
        ResolutionContext context)
    {
        destination = destination ?? new ExpandableReference<TAggregateRoot, TResource>();
        destination.Id = source;
        destination.Obj = ExpandReference(source, context);
        return destination;
    }

    private TResource ExpandReference(int source, ResolutionContext context)
    {
        TAggregateRoot aggregateRoot = repository.SingleOrDefault(source);
        TResource resource = context.Mapper.Map<TResource>(aggregateRoot);
        return resource;
    }
}

3。提供此映射配置

CreateMap(typeof(int), typeof(ExpandableReference<,>)).ConvertUsing(typeof(ReferenceExpander<,>));
ForAllPropertyMaps(
    map => map.SourceType == typeof(int) && map.DestinationPropertyType.GetGenericTypeDefinition() == typeof(ExpandableReference<,>),
    (map, expression) => expression.Condition((agg, res, _, __, ctx) => ctx.ShouldExpand(map.DestinationProperty.Name)));

其中ctx.ShoudExpand(string)检查ctx的Items字典是否包含目标属性名称。当涉及从我的域对象到资源的映射时,控制器负责添加它。

现在问题

GET /api/Products/4711返回

{
  "id":"4711",
  "productName":"flux capacitor",
  "category": null // what?!?!
}

使用category = null因为条件说在映射期间应该省略它。我需要的是一种这样的后备:

ForAllPropertyMaps(
    map => map.SourceType == typeof(int?) && map.DestinationPropertyType.GetGenericTypeDefinition() == typeof(OptionalExpandableReference<,>),
    (map, expression) => expression
        .Condition((agg, res, _, __, ctx) => ctx.ShouldExpand(map.DestinationProperty.Name))
        .Fallback(/*tell AutoMapper to just map the id, omit the obj*/));
  • 将条件逻辑移入ITypeConverter实现将不起作用,因为无法访问目标成员名称。
  • 使用两个映射也不起作用,因为AutoMapper不是以这种方式工作的。如果我们可以在配置期间注入IMapSelectionStrategy以及对MappingContext的访问权限,那将是一个聪明的解决方案。
  • 使用IValueResolver代替ITypeConverter使用开放式泛型减少配置的好方法
  • 想法?

顺便说一下:expression.Condition((agg, res, ???, ???, ctx)的第三个和第四个参数中出现了什么?我从未见过与null不同的东西。

0 个答案:

没有答案