ASP.NET Web API OData: Navigation Links Using Compound Keys

OData questions continue :)

I have an Entity with a compound key, like this one:

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; } } 

I created a CompositeKeyRoutingConvention that handles compound keys for ODataController s. Everything works, except for navigation links, such as:

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

The following error message appears in 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> 

I traced the error message in the ASP.NET source code to the FindMatchingActions method in ApiControllerActionSelector , returning an empty list, but my ASP.NET knowledge ends there.

For reference, this is an implementation of the navigation link action method (in 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; } 

I tried not to set the name in the FromODataUri attribute by setting a lowercase name, all reasonable that I could think of. The only thing I noticed is that using the regular EntitySetController is that the arguments for the key value must be named key (or the FromODataUri attribute must have the Name property set to key ), otherwise it wonโ€™t work. I wonder if something like this is here ...

+1
source share
1 answer

I found that is missing:

In addition to the custom EntityRoutingConvention you will need a custom 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 

And just add this to the top of the conventions as a custom EntityRoutingConvention . Done :)


Update comment below:

You must implement your own NavigationRoutingConvention , which overrides the SelectAction method and breaks the composite keys within the controller context into a key and values. Then you must add them to the route values โ€‹โ€‹yourself.

Finally, in the configuration where you call MapODDataRoute with your custom EntityRoutingConvention already, add the new NavigationRoutingConvention to the legend list.

NavigationRoutingConvention in 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; } } 

Finally, you should add it to the OData route (presumably in App_Start/WebApiConfig.cs ), where you already added EntityRoutingConvention .

+2
source

Source: https://habr.com/ru/post/978041/


All Articles