OData Web API Patch Request with DTO - odata

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

Related

odata and webapi together in the same controller

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

POST to a related collection in WEB API 2 with OData 4

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)
{
}

OData Web API routing

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.

Web Api Help Page, multiple Get same parameter break help generator

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/.

How to only return a subset of specific fields from Model in REST APIController using .NET MVC4

I am writing a Web API that clients can make requests to to return data in XML format. I am implementing this in .NET using Enterprise Foundation and MVC4.
I am struggling a bit with how to only return a subset of some fields from my Models in my Controllers.
For arguments sake, lets say I have a Product model that contains attributes "Id", "Name", "Price" and "Actual Cost" (I am using an example from http://www.asp.net/web-api/overview/creating-web-apis/using-web-api-with-entity-framework/using-web-api-with-entity-framework,-part-6)
I need to expose a Web API for clients to query a specific Product to get its name and price, but in this response I don't want to return the "Actual Cost" property (because this is our secret).
Now in the link I provide this is exactly the problem they are attempting to solve by the use of DTO's (they define a DTO called ProductDTO that contains only the subsets I want to return). I have implemented this solution and I am indeed now able to return only the fields I specify in the DTO.
The problem is that the naming used for the returned entity in XML is now ProductDTO rather than Product, i.e. the returned XML is
{"ProductDTO":[{"Id":1,"Name":"Tomato Soup","Price":1.39}, {"Id":3,"Name":"Yo yo","Price":6.99]}
rather than
{"Product":[{"Id":1,"Name":"Tomato Soup","Price":1.39}, {"Id":3,"Name":"Yo yo","Price":6.99]}
That means that all of our clients currently using our API and expects a "Product" to be returned will now get a "ProductDTO" returned, which means that they will have to make changes to their code and which is unacceptable. I need to provide them with a "Product" with only the relevant set of sub-fields as they are currently getting. How do I achieve this? I cannot simply "ignore" a data member as suggested in prevent property from being serialized in web api because I also have some API cases where I indeed DO need to return ALL the attributes and not only a subset.
Just some background: We have an existing API server interface that was written in Ruby on Rails and we are now moving this over to C# and .NET MVC4. We also have a bunch of client applications already interfacing to our existing, older, Ruby on Rails API Server and we don't want clients to make any changes to their code. We are simply moving our API server code over from Ruby on Rails to C#. In Ruby on Rails I was simply able to apply a filter to the XML Serializer when I need to only return a subset of attributes on certain calls.
If you want to continue down the DTO route that you have started, which IMHO is a good idea as it gives you control of what you export without polluting your internal classes with export specific stuff, you can add a DataContract attribute to your ProductDTO class.
[DataContract(Name="Product")]
public class ProductDTO
{
[DataMember]
public int Id {get;set;}
[DataMember]
public string Name {get;set;}
}
The default XML formatter used in Web API is the DataContractSerializer.
You can read more about this here
Suppose you have a class like
public class product
{
public string Name{get; set;}
..
}
and you don't want to appear it in the response you can just use [XMLIgnore] attribute
[XMLIgnore]
public string Name{get; set;}
hopes this helps.

Resources