【问题标题】:Projection using contextual values in AutoMapper在 AutoMapper 中使用上下文值进行投影
【发布时间】:2013-03-23 21:13:06
【问题描述】:

我目前正在评估 AutoMapper 是否对我们的项目有益。我正在使用 ASP.NET Web API 开发一个 RESTful 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 中可能会失败。

【问题讨论】:

  • 目前我倾向于在呼叫站点创建地图,但我不知道这是否是个好主意。
  • 什么时候定义链接?在运行时?
  • 更准确地说,链接是在 1) 编译时、2) 在启动时还是 3) 在映射/解析时定义的?
  • Url 属性是指每个请求实例化的 UrlHelper 实例,因此在映射时。
  • 我可能会完全避免使用 AutoMapper。这不适合我正在做的事情。

标签: c# automapper automapper-2


【解决方案1】:

这不是一个很好的解决方案,但在阅读完文档后,似乎没有一个......我们目前正在通过将Tuple&lt;TDomainType, TContextStuff&gt; 映射到TDataTransfer 来添加上下文内容。所以在你的情况下你会Mapper.CreateMap&lt;Tuple&lt;Customer, Controller&gt;, CustomerResource&gt;

不漂亮,但它有效。

【讨论】:

  • 不漂亮,但很有创意。我正在考虑尝试这个,但我已经在考虑配置嵌套映射时会造成的混乱。
【解决方案2】:

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

简单示例

你可以定义一个接口来获取你当前的“上下文”(这意味着什么取决于你在做什么以及你是如何实现的,所以在这个例子中,我将只使用当前的 HttpContext,它可以让你访问 Session、Server , 物品等...):

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.Items 集合中:

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 实例。

【讨论】:

  • 您如何获得 IoC 的上下文?假设您在 MVC 控制器中执行 Mapper.Map。您将如何将正在执行的实例注入到解析器/转换器中?
  • @carlpett,我更新了通过工厂获取当前 Http Context 的方法。
  • 但是HttpContext不是我解析时需要的。
  • 您在原始问题中提到的“相关资源链接”的来源是什么?
  • 为请求实例化的 UrlHelper 实例,可通过 ApiController 的 Url 属性获得。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-19
  • 1970-01-01
  • 2012-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多