Web 中批量 OData 请求的 NULL HttpContext API - 错误的设计模式?

NULL HttpContext for batched OData requests in Web API - wrong design pattern?

我正在尝试确定将依赖项注入控制器的正确方法,其中要注入的具体类型是基于路由数据参数的变量。

到目前为止,我已经完成了以下设置,非常适合正常请求:

控制器

 public class OrdersController : ODataController
 {

    private IOrderService ErpService { get; }

    public OrdersController(IOrderService orderService)
    {
        ErpService = orderService;
    }

    [EnableQuery(PageSize = 100)]
    public IQueryable<OrderDto> Get(ODataQueryOptions<OrderDto> queryOptions)
    {
        return ErpService.Orders(queryOptions);
    }

    ...
    // Post
    // Patch/Put
    // Delete
}

使用以下 OData 路由配置,我可以指定路由模板应包含一个 'company' 参数:

配置

config.MapODataServiceRoute( "ODataRoute", "data/{company}", model, new DefaultODataPathHandler(),
                conventions, new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));

这使我可以使用静态方法从 URL:

中读取公司 ID
public static string GetSalesCompanyFromRequest()
{
    var salesCompany = "";
    if (HttpContext.Current == null) return "";

    var routeData = HttpContext.Current.Request.RequestContext.RouteData;
    if (routeData.Values.ContainsKey("company"))
    {
        salesCompany = routeData.Values["company"].ToString();
    }

    return salesCompany;
}

然后,使用 Ninject,我可以选择要使用的 IOrderService 的具体实例(为简洁起见进行了简化):

kernel.Bind<IOrderService>()
            .To<SageOrderService>()
            .When(ctx => GetSalesCompanyFromRequest() == "101").InRequestScope();

kernel.Bind<IOrderService>()
            .To<DynamicsAxOrderService>()
            .When(ctx => GetSalesCompanyFromRequest() == "222").InRequestScope();

kernel.Bind<IOrderService>()
            .To<SapOrderService>()
            .When(ctx => GetSalesCompanyFromRequest() == "333").InRequestScope();

连接器配置

Id          ErpType        ConnectionString
--------------------------------------------
111         Sage           "connectionstring1"
222         DynamicsAx     "connectionstring2"
333         SAP            "connectionstring3"

下面是 URL 的处理方式:

http://odata-demo/data/101/Orders
创建 SageOrderService 并将其注入 OrdersController

http://odata-demo/data/222/订单数
创建 DynamicsAxOrderService 并将其注入 OrdersController

同样的逻辑适用于许多不同的服务,例如:

注: 我选择将公司 ID 放在 URL 中,这样我就可以配置反向代理,将请求转发到更靠近目标数据库的本地 Web 服务器。

在我尝试使用 OData Batching 之前,这一切都完美无缺。 当我发送批处理请求时,似乎没有 HttpContext.Current(它是空的)。

This question 询问类似的问题但不考虑 OData 批处理请求。

Comments 在这个答案中建议通过路由数据注入是代码味道但没有详细说明。

所以,问题是,如何为批处理的 OData 请求获取 HttpContext.Current?或者有没有更好的方法来做我想做的事情?

由于公司已经在路由数据中,我可以向每个单独的操作添加一个额外的公司参数,如下所示,以允许传入公司编号,然后使用工厂获取正确的具体类型:

public class OrdersController : ODataController
{

    [EnableQuery(PageSize = 100)]
    public IQueryable<OrderDto> Get(ODataQueryOptions<OrderDto> queryOptions, string company)
    {

        var erpService = ErpServiceFactory.GetService(company);

        return erpService.Orders(queryOptions);
    }

    ...
    // Post
    // Patch/Put
    // Delete
}

这意味着我还必须在每个有点异味的操作中初始化 OrderService。

我想如果我使用 ActionFilter 来定位并将正确的具体类型传递给操作,这可能不会那么臭:

public class RequiresOrderServiceAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        string salesCompany = "";
        var data = actionContext.Request.GetRouteData();

        if (data.Values.ContainsKey("company"))
        {
            salesCompany = data.Values["company"].ToString();

            var orderService = ErpServiceFactory.GetService(company);

            actionContext.ActionArguments.Add("erpService", orderService);
        }
    }
}

public class OrdersController : ODataController
{

    [EnableQuery(PageSize = 100)]
    public IQueryable<OrderDto> Get(ODataQueryOptions<OrderDto> queryOptions, IOrderService erpService)
    {
        return erpService.Orders(queryOptions);
    }

    ...
    // Post
    // Patch/Put
    // Delete
}

想法?