Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CreateRef method migrated to .Net Core results in 404, how to implement Create Relationships in OData with .Net Core

I have 2 POCOs, Lessons and Traits with int PKs. I have navigation properties set up such that I can successfully $expand like so:

  • http://localhost:54321/odata/Lessons?$expand=Traits
  • http://localhost:54321/odata/Traits?$expand=Lessons

My final hurdle in migrating project from Net 461 to .Net Core 2 is Creating Relationships.

Specifically, when I try to call the following method, with the following request, I get a 404.

[AcceptVerbs("POST", "PUT")]        
public async Task<IActionResult> CreateRef(
    [FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
{
    ....  Do Work
}

Postman request:

http://localhost:54321/odata/Lessons(1)/Traits/$ref

body:

 {
    "@odata.id":"http://localhost:54321/OData/traits(1)"
 }

The following is my Startup.Configure method.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    var builder = ConfigureOdataBuilder(app);

    app.UseMvc(routeBuilder =>
    {
        routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(null).Count();
        routeBuilder.MapODataServiceRoute("ODataRoute", "odata", builder.GetEdmModel());

        // Work-around for #1175
        routeBuilder.EnableDependencyInjection();
        routeBuilder.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}"); // enable mvc controllers
    });            
}

private ODataConventionModelBuilder ConfigureOdataBuilder(IApplicationBuilder app)
{
    var builder = new ODataConventionModelBuilder(app.ApplicationServices);

    builder.EntitySet<Lessons>(nameof(Lessons));
    builder.EntitySet<Traits>(nameof(Traits));       

    return builder;
}

Question: How do I reach this controller method?

Things I have tried,

  • Rename CreateRef to CreateLink and Create
  • Followed every link in these Git Issues, here and here.
  • Read up on Attribute Routing spec.
  • Tried solution based off this DeleteRef in this Web Api oData v4 $ref 404 or server error
  • Tried explicitly defining route with [ODataRoute("Lessons({key})/{navigationProperty}")]
like image 580
ttugates Avatar asked Mar 26 '18 13:03

ttugates


People also ask

What is OData .NET core?

OData stands for Open Data Protocol which helps to build and consuming of RESTFul APIs. It is an ISO/IEC-approved and OASIS standard. OData will take care of various approaches to RESTful API like Status codes, URL conventions, request and response headers, media types, query options, payload formats, etc.

Does OData require Entity Framework?

For this tutorial, we'll use Entity Framework (EF) Code First to create the back-end database. Web API OData does not require EF. Use any data-access layer that can translate database entities into models.

What is OData in Web API c#?

The Open Data Protocol (OData) is a data access protocol for the web. OData provides a uniform way to query and manipulate data sets through CRUD operations (create, read, update, and delete).

Which of the following is the class to derive from for an OData service in Web API?

To define an OData service, simply derive from ODataController. This is a base class for OData controllers that support writing and reading data using the OData formats.


1 Answers

It was a long way, but I finally found the answer.

[ODataRoute("lessons({lessonId})/traits({traitId})/$ref")]
public IActionResult CreateRef([FromODataUri] int lessonId, [FromODataUri] int traitId)
{
    //do work
}

Important: You have to call the id-params as I did. Don´t just call them Id - otherwise you´ll get a 404.

One more thing...

For those who tried the way from the microsoft docs - the Api-Names changed.. You don´t need them for this task, but if you have to convert an Uri to an OData-Path, here is an Uri-Extension doing this for you:

public static Microsoft.AspNet.OData.Routing.ODataPath CreateODataPath(this Uri uri, HttpRequest request)
{
    var pathHandler = request.GetPathHandler();
    var serviceRoot = request.GetUrlHelper().CreateODataLink(
                            request.ODataFeature().RouteName, 
                            pathHandler, 
                            new List<ODataPathSegment>());

    return pathHandler.Parse(serviceRoot, uri.LocalPath, request.GetRequestContainer());
}

If you have an Uri like this: http://localhost:54321/OData/traits(1) you can split this into OData-segments to get e.g. the navigation: returnedPath.NavigationSource or the specified key: returnedPath.Segments.OfType<KeySegment>().FirstOrDefault().Keys.FirstOrDefault().Value

like image 58
Joshit Avatar answered Dec 05 '22 16:12

Joshit