在AutoMapper中使用上下文值的投影

本文关键字:投影 上下文 AutoMapper | 更新日期: 2023-09-27 18:06:57

我目前正在评估AutoMapper是否能对我们的项目有益。我正在使用ASP开发RESTful Web API。NET Web API,我必须返回的东西之一是包含链接的资源。考虑这个简化的示例,使用以下域对象:

public class Customer
{
    public string Name { get; set; }
}

我需要将其映射到一个资源对象,有点像DTO,但添加了属性以方便REST。这就是我的资源对象的样子:

public class CustomerResource
{
    public string Name { get; set; }
    public Dictionary<string, string> Links { get; set; }
}

Links属性需要包含指向相关资源的链接。现在,我可以使用以下方法构造它们:

public IEnumerable<CustomerResource> Get()
{
    Func<Customer, CustomerResource> map = customer => 
        new CustomerResource
        {
            Name = customer.Name,
            Links = new Dictionary<string, string>()
            {
                {"self", Url.Link("DefaultApi", new { controller = "Customers", name = customer.Name })}
            }
        }
    var customers = Repository.GetAll();
    return customers.Select(map);
}

…但这很乏味,而且我有很多嵌套的资源等等。我看到的问题是,我不能使用AutoMapper,因为它不允许我提供投影期间所需的某些东西,这些东西的作用域限定在执行映射操作的点上。在这种情况下,ApiController的Url属性提供了UrlHelper实例,我需要它来为我创建链接,但可能还有其他情况。

你会如何解决这个难题?

注:我专门为这个问题输入了这个代码,它在你的头脑中编译,但在你最喜欢的IDE中可能会失败。

在AutoMapper中使用上下文值的投影

这不是一个漂亮的解决方案,但通过阅读文档后,似乎没有一个…我们现在通过将Tuple<TDomainType, TContextStuff>映射到TDataTransfer来添加上下文内容。所以在你的例子中,你是Mapper.CreateMap<Tuple<Customer, Controller>, CustomerResource>

我会考虑使用自定义类型转换器。类型转换器可以通过IOC容器注入上下文信息。或者,由于转换器是在配置时实例化的,因此它可以有一个对工厂的引用,该工厂将在每次运行类型转换器时返回上下文信息。

你可以定义一个接口来获取你当前的"上下文"(这意味着什么取决于你在做什么,以及你如何实现的东西,所以在这个例子中,我只是当前的HttpContext,它可以让你访问Session, Server, Items等…):

public interface IContextFactory
{
    HttpContext GetContext();
}

实现很简单:

public class WebContextFactory : IContextFactory
{
    public HttpContext GetContext()
    {
        return HttpContext.Current;
    }
}

您的自定义类型转换器可以从您的IOC容器中获取一个IContextFactory实例,并且每次映射运行时,您可以调用GetContext()来获取当前请求的上下文。

访问Url属性

UrlHelper来自附加到当前控制器上下文的Request对象。不幸的是,这在HttpContext中不可用。然而,你可以覆盖ApiController上的Initialize方法,并将controllerContext存储在HttpContext中。项目收集:

protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
    HttpContext.Current.Items["controllerContext"] = controllerContext;
    base.Initialize(controllerContext);
}

你可以从当前的HttpContext中访问它:

var helper = ((HttpControllerContext) HttpContext.Current.Items["controllerContext"]).Request.GetUrlHelper();

我不确定这是最好的解决方案,但它可以让您的UrlHelper实例在您的自定义类型映射器