I want to have a route like:
/Accounts(id)/Orders
where I can execute a POST to create an order. I can't find a way to add this route using OData in WebApi. For GET there is a convention to follow to get related collections, but I am not able to find any convention for posting new entities to a related collection.
Is there a standard way to handle this POST request with Web API 2 and OData 4 ?
Added the following attributes to the method and it worked:
[HttpPost]
[ODataRoute("Accounts({key})/Orders")]
public IHttpActionResult Orders([FromODataUri] string key, OrderDto orderDto)
{
}
Related
I have the ticketController
public class ticketsController : ODataController
{
}
Is it possible to have this:
GET /api/tickets?$skip={record_to_skip}&$top={pageSize}
GET /api/tickets/{id}
PUT /api/tickets/{id}
DELETE /api/request/{id}
First GET method uses odata routing, the second GET, PUT, and POST uses web api routing
Thanks
Yes it is possible but you have to use an OData attribute. Your controller should inherit from ApiController not ODataController and on the method/action you want to query paint it with the OData 'Queryable' attribute, check this out for more details:
https://ovaismehboob.wordpress.com/2014/01/18/adding-queryable-support-to-asp-net-web-api-controller-action-methods/
Also if you search for OData queryable attribute you should get a a load of information back
I have a web API exposing ODATA from a SQL stored proc. I want to use a url like /odata/firmhierarchy(225) to pass 225 into a param for the stored proc. It just tells me that it can't find a matching resource. It hits the controller, just skips the method. Thoughts?
In webapiconfig
private static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Employee>("Employees");
builder.EntitySet<Employee>("FirmHierarchy");
return builder.GetEdmModel();
}
Context:
public virtual ObjectResult<Employee> sp_EmployeeHierarchy(Nullable<int> managerEmpID)
{
var managerEmpIDParameter = managerEmpID.HasValue ?
new SqlParameter("ManagerEmpID", managerEmpID) :
new SqlParameter("ManagerEmpID", 0);
return ((IObjectContextAdapter)this).ObjectContext.ExecuteStoreQuery<Employee>("sp_EmployeeHierarchy #ManagerEmpID", managerEmpIDParameter);
}
Only method in controller:
[Queryable]
public IQueryable<Employee> GetFirmHierarchy()
{
return db.sp_EmployeeHierarchy(225).AsQueryable();
//return SingleResult.Create(db.Employees.Where(employee => employee.EmpId == key));
}
This should work:
1.Write another method in your controller:
[EnableQuery]
public IQueryable<Employee> Get([FromODataUri] int key)
{
return db.sp_EmployeeHierarchy(key).AsQueryable();
}
Please note that [EnableQuery] is an attribute introduced in Web API for OData V4. If you are still using Web API for OData V1-3, use [Queryable] still.
2.Then you can send the request
GET /odata/firmhierarchy(225)
and get the employees.
I was able to make ODATA work for a table, when auto-generated from entity framework. However, that generation process didn't want to work for a complex type returned by a Table Valued Function (similar scenario to a SP), because it didn't seem to understand where the key was.
What I found was that I could however make it work. First, I check out this article. He sets things up a bit more manually, where his Get on a companyProcessingController ends up routing for id 3 as "http://localhost:10020/odata/companyProcessing(3)" .
This surprised me. My other generated classes set up the pattern that SomeEntity became SomeEntityController, with methods like GetSomeEntities, and a routing that seemed to me to match the method but dropping the word get. Therefore, dropping the entity name from the Get method name seemed different, but it worked. Proving that the path is actually matching the controller name, not the method name.
In this Case you configure the routing using the data type you're querying for, and the beginning of the controller name. Then the actual path utilizes the beginning of the controller name as well.
And then all of this just brings us essentially to the other posted solution, assuming your controller name is firmhierarchyController
So, now, making sense of this... Try going to http://localhost:55063/odata/$metadata , where your port may differ. You'll notice that ODATA exposes a DataType, which is accessed via a DataSet. When a client tries to query into ODATA, they are trying to query against the DataSet, getting items of the DataType.
The DataSet matching the controller name (less Controller), and the Get methods can indeed just be Get without further extension of the name - and otherwise in this scenario was giving me problems.
I have created an OData Controller that exposes three different GET methods.
// http://odata/Companies
IQueryable<Company> Get()
// http://odata/Companies(1)
SingleResult<Company> Get([FromODataUri] key)
// http://odata/Companies(1)/Persons
IQueryable<Persons> GetPersons([FromODataUri] key)
Using this approach the OData Links convention works perfectly but the Help Generator creates only 1 method, the Get(), while it skips Get{key} and GetPersons{key}
If I change the methods in this way:
// http://odata/Companies
IQueryable<Company> Get()
// http://odata/Companies(1)
SingleResult<Company> Get([FromODataUri] key)
// http://odata/Companies(1)/Persons
IQueryable<Persons> GetPersons([FromODataUri] companyKey)
the help is generated but Web Api OData doesn't work anymore cause the framework is looking for a "key" parameter.
If you are using OData v4(System.Web.OData.dll instead of System.Web.Http.OData.dll), you can resolve this issue through attribute routing:
[ODataRoute("Companies({companyKey})/Persons")]
IQueryable<Persons> GetPersons([FromODataUri] companyKey)
For more info about attribute routing, you can reference https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataAttributeRoutingSample/.
Rather than expose my Database Model I'm trying to get my WebAPI endpoints working with DTO equivalents. In my WebAPIConfig referenced from the Global.asax file I have:
builder.EntitySet<ProductDTO>("Products");
builder.EntitySet<Product>("ProductsDB");
Where Product has an extra field that ProductDTO does not have.
My patch request is currently:
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] int key, Delta<ProductDTO> patch)
{
I can see that essentially I need to convert my Delta<ProductDTO> into a Delta<Product> but I cannot see any methods in the Delta class that would allow me to set its underlying EntityType instance.
Does anyone have any advice?
This question is very similar to yours, please reference it:
Microsoft Odata api through a ViewModel has problems in PATCH
Background:
In MVC3, I've used the following syntax to specify custom Action parameter names:
public ActionResult ActionName([Bind(Prefix = "principalID")] int userID,
[Bind(Prefix = "dependentID")] long applicationID)
The route for this action was defined as follows (ActionNameConstraint is a custom IRouteConstraint):
routes.MapHttpRoute(
"DependantAction",
"{controller}/{principalID}/{action}/{dependentID}",
new {controller = #"[^0-9]+", action = ActionNameConstraint.Instance, dependentID = RouteParameter.Optional}
);
Question:
The BindAttribute is a System.Web.Mvc class. Is there an equivalent of this (parameter binding) in Web Api?
Of course, if there are other solutions to achieve the same result, I'd love to hear them!
You can use the System.Web.Http.FromUriAttribute attribute to specify the parameter names used for model binding.
public ActionResult ActionName(
[FromUri(Name = "principalID.userID")] int userID,
[FromUri(Name= "dependentID.applicationID")] long applicationID
)
FromUri tells model binding to examine the query string and the RouteData for the request.
I understand that in WebAPI you just use special controller class base, and special Action names, but still after all they are controllers and actions.
Did you try it and it didn't work? A quick look at this article seems to suggest model binding in general (not the specific attribute though) should work normally:
http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx
Edit:
Some say this should work with MVC only and downvoted the answer. Here you go, more references:
MS Documentation:
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
MSDN Blog Post
http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx
Accepted SO answer
Custom Parameter Names With Special Characters in ASP.NET Web API MVC 4
A post exploring several approaches (follow to the end)
http://blogs.msdn.com/b/jmstall/archive/2012/04/20/how-to-bind-to-custom-objects-in-action-signatures-in-mvc-webapi.aspx
Another MSDN blog post (see scenario 3)
http://blogs.msdn.com/b/hongmeig1/archive/2012/09/28/how-to-customize-parameter-binding.aspx