0
votes

I'am getting crazy here. I tried a lot of different approaches that I found through out the web. However cannot seem to get this work. Here is the last thing I tried.

modelBuilder.EntitySet<User>("Users").EntityType.Action("IsEmailAvailable")
.Returns<bool>()
.Parameter<string>("email");

I configure my entity like this.

[HttpPost]
public bool IsEmailAvailable(ODataActionParameters parameters)
{
    return true;
}

And this is the action in my controller.

When I do something like

http://localhost:11111/odata/Users

I gets the entites as expected. However if I do

http://localhost:11111/odata/Users/IsEmailAvailable

I got the error:

Message: "The OData path is invalid.", ExceptionMessage: "Invalid action detected. 'IsEmailAvaliable' is not an action that can bind to 'Collection([App.Models.Models.User Nullable=False])'.", ExceptionType: "Microsoft.Data.OData.ODataException", StackTrace: " at System.Web.Http.OData.Routing.DefaultODataPathHandler.ParseAtEntityCollection(IEdmModel model, ODataPathSegment previous, IEdmType previousEdmType, String segment) at System.Web.Http.OData.Routing.DefaultODataPathHandler.ParseAtCollection(IEdmModel model, ODataPathSegment previous, IEdmType previousEdmType, String segment) at System.Web.Http.OData.Routing.DefaultODataPathHandler.ParseNextSegment(IEdmModel model, ODataPathSegment previous, IEdmType previousEdmType, String segment) at System.Web.Http.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String odataPath) at System.Web.Http.OData.Routing.ODataPathRouteConstraint.Match(HttpRequestMessage request, IHttpRoute route, String parameterName, IDictionary`2 values, HttpRouteDirection routeDirection)"

3
Can you post your /$metadata page please?TomDoesCode
I have no idea what that is? :/Ege Aydın
Well there is a lot of things written in there since I have other entites. What I should be looking for?Ege Aydın

3 Answers

3
votes

I tested in my sample project, it should work. See my newest commit.

In WebApiConfig.cs, I added two actions using codes similar with yours:

builder.EntitySet<Customer>("Customers")
    .EntityType.Collection.Action("IsEmailAvailable")
    .Returns<bool>()
    .Parameter<string>("email");

builder.EntitySet<Customer>("Customers")
    .EntityType.Action("IsEmailAvailable").Returns<bool>()
    .Parameter<string>("email");

Where,

  1. the first one is bound to collection of entity type.
  2. the second one is bound to entity type.

In the CustomersController, there are two methods:

[HttpPost]
public bool IsEmailAvailable(ODataActionParameters parameters)
{
    ...
}

[HttpPost]
public IHttpActionResult IsEmailAvailable(int key, ODataActionParameters parameters)
{
    ...
}

Where,

  1. the action bounded to collection of entity type will route to the first method.
  2. the action bounded to entity type will route to the second method.

For the detail implementation, please refer to my project.

Test

Bound to collection

  1. Return "True"

Request:

POST http://localhost:33082/odata/Customers/Extra.IsEmailAvailable
Content-Type: application/json
Content: 
{"email":"[email protected]"}

Response:

{
  "@odata.context":"http://localhost:33082/odata/$metadata#Edm.Boolean","value":true
}
  1. Return "False"

Request:

POST http://localhost:33082/odata/Customers/Extra.IsEmailAvailable
Content-Type: application/json
Content: 
{"email":"[email protected]"}

Response:

{
  "@odata.context":"http://localhost:33082/odata/$metadata#Edm.Boolean","value":false
}

Bound to entity type

  1. Return NotFound (in my implementation, I harded the key must be "3", otherwise, it returns "NotFound (404)"

Request:

POST http://localhost:33082/odata/Customers(2)/Extra.IsEmailAvailable
Content-Type: application/json
Content: 
{"email":"[email protected]"}

Response:

HTTP/1.1 404 Not Found
  1. Return a string (in my implementation, I harded to return a string if the key is 3, and the email is "[email protected]")

Request:

POST http://localhost:33082/odata/Customers(3)/Extra.IsEmailAvailable
Content-Type: application/json
Content: 
{"email":"[email protected]"}

Response:

{
  "@odata.context":"http://localhost:33082/odata/$metadata#Edm.String","value":"Your input email is :[email protected]"
}
  1. Return False. It's similar, omit it.

Besides, there are several materials you can refer to:

  1. http://blogs.msdn.com/b/odatateam/archive/2014/12/08/function-amp-action-in-web-api-v2-2-for-odata-v4-0-type-scenario.aspx
  2. http://odata.github.io/WebApi/#04-07-action-parameter-support

Hope it can help you. Thanks.

1
votes

Ege

The error message below is meaningful. It tells that "IsEmailAvaliable" can be called along with Collection.

IsEmailAvaliable' is not an action that can bind to 'Collection([App.Models.Models.User Nullable=False])'

From your model build codes:

modelBuilder.EntitySet<User>("Users").EntityType.Action("IsEmailAvailable")
.Returns<bool>()
.Parameter<string>("email");

You define the IsEmailAvailable bind to User type, not collection of User type.

There are two ways to make it work:

  1. Keep the model build codes unchanged, but you should call it as:

    http://localhost:11111/odata/Users(key)/Namespace.IsEmailAvailable

  2. Modify the model build codes as:

    modelBuilder.EntitySet("Users").EntityType.Collection.Action("IsEmailAvailable") .Returns() .Parameter("email");

Note: I add Collection before call Action. Then you can call it as:

http://localhost:11111/odata/Users/Namespace.IsEmailAvailable

Note: Namespace is necessary if you are using OData V4 related library.

1
votes

Allright, after calming down, because I got crazy why this was not working, I started to think and find the cause of the problem. Somehow I installed both V1-3 and V4 versions of Odata. So when I was using builders it was getting confused. So I unistalled the V1-3 and fixed the compiler errors and It is working fine. Thanks to Sam Xu for his efforts.