Reputation: 324
Am I able to make this prittier with custom model/class?
"@odata.context": "https://localhost:5001/api/$metadata#Customers",
"@odata.count": 4830,
"value": [
{
"Id": 1,
"Code": "",
"Name1": "",
"Name2": "",
"Name3": ""
},
...
I don't need @data.context
and I want to be able to add response message for zero results.
For example:
"responseMessage": "Example message from API with OData",
"responseCode": 10,
"itemsLength": 4830,
"items": [
{
"Id": 1,
"Code": "",
"Name1": "",
"Name2": "",
"Name3": ""
},
...
Also I want to handle exeptions with same model
For example:
"responseMessage": "[Exception.Message] - Exception message example text.",
"responseCode": -1,
"itemsLength": 0,
"items": []
Upvotes: 1
Views: 914
Reputation: 324
I've found the solution.
OData works best with EF as it "forces" to use IQueryable
instead of IEnumerable
. In my case, I can't use EF because it's not my database, so I'm using Dapper. We all know that Dapper lives by different laws and IQueryable
does not provide any benefits but thanks to this it is possible to return a custom response using OData:
MODEL
public class CustomersResponse
{
public string ReponseMessage { get; set; }
public int ResponseCode { get; set; }
public int ItemsLength { get; set; }
public IQueryable Items { get; set; }
}
KONTROLER
[HttpGet]
public async Task<IActionResult> Get(ODataQueryOptions<Customer> options)
{
var response = new CustomersResponse();
try
{
var customers = await _dataReader.GetCustomers("database_name");
var customersResult = options.ApplyTo(customers.AsQueryable()).Cast<Customer>();
if(customersResult.Count() == 0)
{
response.ItemsLength = customers.Count();
response.ReponseMessage = "NotFound";
response.ResponseCode = 404;
response.Items = customersResult;
return NotFound(response);
}
response.ItemsLength = customers.Count();
response.ReponseMessage = "OK";
response.ResponseCode = 200;
response.Items = customersResult;
return Ok(response);
}
catch (Exception ex)
{
response.ResponseCode = 500;
response.Items = null;
response.ReponseMessage = ex.Message;
response.ItemsLength = 0;
return StatusCode(500, response);
}
}
We remove the [EnableQuery]
attribute from the endpoint, add the ODataQueryOptions<Customer> options
parameter and use options.ApplyTo()
, which will prepare the data accordingly. I would like to remind you that this is on average efficient, because we collect all customers in the first line, and then we filter using OData.
WORKS
.Filter()
.OrderBy()
.SetMaxTop(50)
DOESN'T WORK
.Count() // we don't need that anymore
.Expand()
.Select()
In the case of Select()
there is most likely a problem with the returned data as OData tries to convert it to its type somehow.
{
"reponseMessage": "Unable to cast object of type 'Microsoft.AspNetCore.OData.Query.Wrapper.SelectSome`1[OptimoLogic.Models.Customer]' to type 'OptimoLogic.Models.Customer'.",
"responseCode": 500,
"itemsLength": 0,
"items": null
}
Upvotes: 1