1

OData 问题不断涌现 :)

我有一个带有复合键的实体,如下所示:

public class Entity
{
    public virtual Int32  FirstId  { get; set; }
    public virtual Guid   SecondId { get; set; }
    public virtual First  First    { get; set; }
    public virtual Second Second   { get; set; }
}

我创建了一个CompositeKeyRoutingConvention来处理ODataControllers 的复合键。一切正常,除了像这样的导航链接:

http://localhost:51590/odata/Entities(FirstId=1,SecondId=guid'...')/First

我在 Firefox 中收到以下错误消息:

<?xml version="1.0" encoding="utf-8"?>
<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <m:code />
  <m:message xml:lang="en-US">No HTTP resource was found that matches the request URI 'http://localhost:51950/odata/Entities(FirstId=1,SecondId=guid'a344b92f-55dc-45aa-b92f-271d74643493')/First'.</m:message>
  <m:innererror>
    <m:message>No action was found on the controller 'Entities' that matches the request.</m:message>
    <m:type></m:type>
    <m:stacktrace></m:stacktrace>
  </m:innererror>
</m:error>

我将 ASP.NET 源代码中的错误消息跟踪到ApiControllerActionSelector 中的 FindMatchingActions 方法返回一个空列表,但我对 ASP.NET 的了解到此为止。

作为参考,这是导航链接操作方法的实现(在 an 中ODataController):

public First GetFirst(
    [FromODataUri(Name = "FirstId")] Int32 firstId, 
    [FromODataUri(Name = "SecondId")] Guid secondId)
{
    var entity = repo.Find(firstId, secondId);
    if (entity == null) throw new HttpResponseException(HttpStatusCode.NotFound);
    return entity.First;
}

我尝试不在FromODataUri属性上设置名称,设置小写名称,我能想到的一切都是明智的。我唯一注意到的是,在使用常规时EntitySetController,必须命名键值的参数key(或者FromODataUri属性必须将 Name 属性设置为key),否则它将不起作用。我想知道这里是否也有类似的情况......

4

1 回答 1

2

我发现缺少的东西:

除了自定义之外EntityRoutingConvention,您还需要自定义NavigationRoutingConvention

type CompositeKeyNavigationRoutingConvention () =
    inherit NavigationRoutingConvention ()

    override this.SelectAction (odataPath, controllerContext, actionMap) =
        match base.SelectAction (odataPath, controllerContext, actionMap) with
        | null -> null
        | action ->
            let routeValues = controllerContext.RouteData.Values
            match routeValues.TryGetValue ODataRouteConstants.Key with
            | true, (:? String as keyRaw) ->
                keyRaw.Split ','
                |> Seq.iter (fun compoundKeyPair ->
                    match compoundKeyPair.Split ([| '=' |], 2) with
                    | [| keyName; keyValue |] ->
                        routeValues.Add (keyName.Trim (), keyValue.Trim ())
                    | _ -> ()
                )
            | _ -> ()
            action

只需将其添加到 custom 等约定的前面EntityRoutingConvention。完毕 :)


更新以下评论:

您必须实现自己的NavigationRoutingConvention覆盖SelectAction方法并将控制器上下文中的复合键拆分为键和值。然后您必须自己将它们添加到路由值中。

最后,在MapODDataRoute您已经使用自定义调用的配置中EntityRoutingConvention,将新NavigationRoutingConvention的添加到约定列表中。

C# 中的导航路由约定:

public class CompositeKeyNavigationRoutingConvention : NavigationRoutingConvention
{
    public override String SelectAction(System.Web.OData.Routing.ODataPath odataPath, HttpControllerContext controllerContext, ILookup<String, HttpActionDescriptor> actionMap)
    {
        String action = base.SelectAction(odataPath, controllerContext, actionMap);

        // Only look for a composite key if an action could be selected.
        if (action != null)
        {
            var routeValues = controllerContext.RouteData.Values;

            // Try getting the OData key from the route values (looks like: "key1=value1,key2=value2,...").
            Object keyRaw;
            if (routeValues.TryGetValue(ODataRouteConstants.Key, out keyRaw))
            {
                // Split the composite key into key/value pairs (like: "key=value").
                foreach (var compoundKeyPair in ((String)keyRaw).Split(','))
                {
                    // Split the key/value pair into its components.
                    var compoundKeyArray = compoundKeyPair.Split(new[] { '=' }, 2);
                    if (compoundKeyArray.Length == 2)
                        // Add the key and value of the composite key to the route values.
                        routeValues.Add(compoundKeyArray[0].Trim(), compoundKeyArray[1].Trim());
                }
            }
        }

        return action;
    }
}

最后,您必须将其添加到 OData 路由(大概在 中App_Start/WebApiConfig.cs),您已经在其中添加了EntityRoutingConvention.

于 2013-09-12T14:33:26.680 回答