This article transferred from: http://chris.eldredge.io/blog/2014/04/24/Composite-Keys/
In our basic configuration we told the model builder, which our entity have a composite key comprised of an ID and a version:
123456789 |
PublicvoidMapdataserviceroutes(HttpconfigurationConfig){ VarBuilder=NewOdataconventionmodelbuilder(); VarEntity=Builder.EntitySet<odatapackage> ( "Packages" ); entity. Entitytype. Haskey (pkg => pkg. Id); entity. Entitytype. Haskey (pkg => pkg. Version); //snip}
|
This is enough for our OData feeds to render and links for each edit
self
individual entity in a form like:
http://localhost/odata/Packages(Id='Sample',Version='1.0.0')
But if we navigate to this URL, instead of getting just this one entity by key, we get back the entire entity set.
To get the correct behavior, first we need a override on our packagesodatacontroller that gets an individual entity Insta NCE by Key:
123456789-ten-19 - |
PublicClassPackagesodatacontroller:Odatacontroller{ PublicImirroringpackagerepositoryRepository{Get;Set;} PublicIQueryable<Odatapackage>Get() { ReturnRepository.Getpackages().Select(P=P.Toodatapackage()).AsQueryable(); } PublicIhttpactionresultGet([Fromodatauri]StringId,[Fromodatauri]StringVersion) { var package = repository.findpackage (idversion ); return package == null (ihttpactionresult) notfound () Span class= "line" > : ok (package.< span class= "n" >toodatapackage ()); }} /span>
|
However, out of the The box WebApi OData doesn ' t know how to bind composite key parameters to a action such as this, since th E key is comprised of multiple values.
We can fix this by creating a new routing convention, binds the stuff inside the parenthesis to our route data map:
123456789101112131415161718192021st222324252627 28293031 323334 35363738 394041 42434445 464748 49505152 |
PublicClassCompositekeyroutingconvention:Iodataroutingconvention{ PrivateReadOnlyEntityroutingconventionEntityroutingconvention= NewEntityroutingconvention(); PublicVirtualStringSelectcontroller( OdatapathOdatapath, HttprequestmessageRequest) { ReturnEntityroutingconvention .Selectcontroller(Odatapath,Request); } PublicVirtualStringSelectAction( OdatapathOdatapath, HttpcontrollercontextControllerContext, ILookup<String,Httpactiondescriptor>ActionMap) { VarAction=Entityroutingconvention .SelectAction(Odatapath,ControllerContext,ActionMap); If(Action==Null) { ReturnNull; } VarRoutevalues=ControllerContext.Routedata.Values; ObjectValue; If(!Routevalues.TryGetValue(Odatarouteconstants.Key, OutValue)) { ReturnAction; } VarCompoundkeypairs=((String)Value).Split(','); If(!Compoundkeypairs.Any()) { ReturnNull; } VarKeyValues=Compoundkeypairs .Select(Kv=Kv.Split('=')) .select (kv => new keyvaluepair<string Span class= "kt" >object> (kv[0 ], kv[1]); routevalues. Addrange (keyvalues return action; }} /span>
|
This class decorates a standard and EntityRoutingConvention
splits the raw key portion of the URIs into key/value pairs and adds them all to The Routevalues dictionary.
Once this was done by the standard action resolution kicks in and finds the correct action overload to invoke.
This routing convention is adapted from the WebApi odatacompositekeysampleproject.
Here we see another difference between WebApi OData and WCF Data Services. In WCF Data Services, the framework handles generating a query, which selects a single instance from an IQueryable
. This limits we ability to customize how finding a instance by key was done. In WebApi OData, we had to explicitly define a overload that gets a entity instance by key, giving us + control over How the query is executed.
This distinction might isn't matter for most projects, but in the case of NUGET.LUCENE.WEB, it enables a Mirror-on-demand CA pability where a local feed can fetch a package from another server on the fly, add it to the local repository and then send It back to the client as if it is always there in the first place.
To customize the This on WCF Data Services required significant back flips.
Series Index
- Introduction
- Basic WebApi OData
- Composite Keys
- Default Streams