Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Web API OData Action on the EDM Model Root

I'm building a Web API service using OData, and would like to expose a method as an Action in the service as follows.

http://myServer/odata/myAction

I'm currently mapping the OData routes as follows:

Dim modelBuilder As ODataModelBuilder = New ODataConventionModelBuilder
modelBuilder.EntitySet(Of Product)("Products")

Dim myAction = modelBuilder.Action("myAction")
myAction.Parameter(Of String)("Parameter1")
myAction.Returns(Of Boolean)()

Dim model As IEdmModel = modelBuilder.GetEdmModel
config.Routes.MapODataRoute("ODataRoute", "odata", model)

This wonderful tutorial shows how to associate an action with an entity like this:

http://myServer/odata/Products(1)/myAction

Following the tutorial, I can then write the method for the action in the ProductsController class after creating the model with the following line:

Dim myAction = modelBuilder.Entity(Of Product).Action("myAction")

However, if I don't want to associate the action with an entity, where would I write the method for the action? Is there a DefaultController class I need to write?

like image 848
MCattle Avatar asked Feb 16 '23 22:02

MCattle


1 Answers

We currently do not have support for this out of the box, but its very easy to do it yourself. Example below (This nice sample is actually from Mike Wasson which is yet to be made public :-))

------------------------------------------------------
// CreateMovie is a non-bindable action. 
// You invoke it from the service root: ~/odata/CreateMovie
ActionConfiguration createMovie = modelBuilder.Action("CreateMovie");
createMovie.Parameter<string>("Title");
createMovie.ReturnsFromEntitySet<Movie>("Movies");

// Add a custom route convention for non-bindable actions.
// (Web API does not have a built-in routing convention for non-bindable actions.)
IList<IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault();
conventions.Insert(0, new NonBindableActionRoutingConvention("NonBindableActions"));

// Map the OData route.
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model, new DefaultODataPathHandler(), conventions);

--------------------------------------------------------------

// Implements a routing convention for non-bindable actions.
// The convention maps "MyAction" to Controller:MyAction() method, where the name of the controller 
// is specified in the constructor.
public class NonBindableActionRoutingConvention : IODataRoutingConvention
{
    private string _controllerName;

    public NonBindableActionRoutingConvention(string controllerName)
    {
        _controllerName = controllerName;
    }

    // Route all non-bindable actions to a single controller.
    public string SelectController(ODataPath odataPath, System.Net.Http.HttpRequestMessage request)
    {
        if (odataPath.PathTemplate == "~/action")
        {
            return _controllerName;
        }
        return null;
    }

    // Route the action to a method with the same name as the action.
    public string SelectAction(ODataPath odataPath, System.Web.Http.Controllers.HttpControllerContext controllerContext, ILookup<string, System.Web.Http.Controllers.HttpActionDescriptor> actionMap)
    {
        if (controllerContext.Request.Method == HttpMethod.Post)
        {
            if (odataPath.PathTemplate == "~/action")
            {
                ActionPathSegment actionSegment = odataPath.Segments.First() as ActionPathSegment;
                IEdmFunctionImport action = actionSegment.Action;

                if (!action.IsBindable && actionMap.Contains(action.Name))
                {
                    return action.Name;
                }
            }
        }
        return null;
    }
}

--------------------------------------------------

// Controller for handling non-bindable actions.
[ODataFormatting]
[ApiExplorerSettings(IgnoreApi = true)]
public class NonBindableActionsController : ApiController
{
    MoviesContext db = new MoviesContext();

    [HttpPost]
    public Movie CreateMovie(ODataActionParameters parameters)
    {
        if (!ModelState.IsValid)
        {
            throw new HttpResponseException(HttpStatusCode.BadRequest);
        }

        string title = parameters["Title"] as string;

        Movie movie = new Movie()
        {
            Title = title
        };

        db.Movies.Add(movie);
        db.SaveChanges();
        return movie;
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}    
like image 107
Kiran Avatar answered Feb 23 '23 09:02

Kiran