I am starting to use OData in my MVC4 application and the problem that I am having, is that I cannot perform any sort or filter operations on my IQueryable because I am using complex objects. Below is a simple example of something I am trying to accomplish:
My API Controller is attempting to return a collection of MyObjects as IQueryable.
public IQueryable Get()
{
List<MyObject> myObjects = GetMyObjects();
return myObjects.AsQueryable() ;
}
Each MyObject contains an InnerObject that has the properties I want to sort and/or filter on.
public class MyObject
{
[Key]
public MyInnerObject innerObject{ get; set; }
public MyObject(Dictionary<string, object> value)
{
innerObject= new MyInnerObject(){
item = value["item"].ToString(),
itmdesc = value["itmdesc"].ToString()
};
}
}
public class MyInnerObject
{
public string item { get; set; }
public string itmdesc { get; set; }
}
I can successfully execute the top commands through the url
localhost:5050/Test/Get?$top=10
But I really want to be able to sort my results using
localhost:5050/Test/Get?$top=10&$orderby=innerObject.item
I have tried
localhost:5050/Test/Get?$top=10&$orderby=innerObject.item
localhost:5050/Test/Get?$top=10&$orderby=innerObject/item
localhost:5050/Test/Get?$top=10&$orderby=item
Any suggestions?
EDIT:
I should mention that it works if I put the item and itmdesc properties within MyObject, but for my purposes (this is just a minified version of my complex entities), they will need to be wrapped in a complex type.
In my Api controller, I have also tried IQueryable< MyObject> but that doesnt make a difference
In general, OData as a protocol allows the second thing you tried (localhost:5050/Test/Get?$top=10&$orderby=innerObject/item). It is likely that this is a temporary limitation of the Web API implementation of OData (assuming that's what you're using based on the rest of your environment and the returning of IQueryable).
Aspnet Web API OData doesn't support ordering by nested properties or expressions. There is an issue open on codeplex for supporting ordering by nested properties.
However if you are slightly adventurous you can use ODataQueryOptions to model bind the individual odata query options and then translate $orderby AST to a linq expression and apply it manually to your IQueryable.
Related
I currently have an C# WebAPI that uses a version of OData that we wrote. We want to start using Microsoft's OData4 which can do more then our custom implementation.
Creating a controller that extends the ODataController I can create a controller that automatically queries based on the query string. (Shown below)
The problem is that it returns the results of the query when I want it to return the Result object which includes additional data. When I set the return type to Result though it will no longer apply the query string.
How can I use the automatic queryable implementation and still return my own object? I've tried making a public method that returns the correct object and calls a private method returning the queryable but it doesn't filter the queryable correctly.
Am I on the right track, or are there other options?
public class CallController : ODataController
{
[EnableQuery]
public IQueryable<Call> GetCall()
{
var list = new List<Call>
{
new Call
{
Id = 4
},
new Call
{
Id = 9
},
new Call
{
Id = 1
}
};
return list.AsQueryable();
}
}
public class Call
{
public int Id { get; set; }
}
public class Result
{
public Call[] Calls { get; set; }
public string NewToken { get; set; }
public string Warning { get; set; }
}
Use ODataQuertOptions instead of [EnableQuery] attribute. Check https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options#invoking-query-options-directly
You would need to intercept the response with an action filter attribute in the onactionexecuted and convert the value to whatever you want. It wouldn't be pretty since it wouldn't be clear what the method was truly returning. But I don't see any other option with odata as the result must be iquerable.
Under the hood, the EnableQuery attribute is executing the action it decorates to get an IQueryable<T>, converting the odata query into something that can be applied to the IQueryable<T>, applying it and returning the results.
In order to work, it needs an IQueryable<T>.
The ODataQueryOptions and example in Ihar's answer may give you what you want, but for various reasons it wasn't as useful to me as EnableQuery and so I ended up with an output formatter.
You can inspect the first output formatters in services.AddMvc(options => { options.OutputFormatters } in Startup.ConfigureServices and see that the first one has a bunch of different payload kinds.
I have been able to insert a custom error handler to handle ODataPayloadKind.Error payloads - re-writing the content returned from the server to remove stack traces etc if not in dev mode. I haven't looked into non-error cases, but you may be able to use what I have here as a starting point.
I need to add filtering to my API requests that support AutoQuery, so based on this SO answer, used q.And to add conditions. The issue is that one of the POCO properties is a List<string> and it seems doing a simple Contains() won't work. Here's a simple example of what I have:
public class PocoObject
{
public int Id { get; set; }
public List<string> Names { get; set; }
}
My service looks like this:
public object Get(PocoObjects request)
{
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
if (someCondition)
{
q.And(x => x.Names.Contains(request.TargetName));
}
return AutoQuery.Execute(request, q);
}
Problem is, I get an error like this:
variable 'x' of type 'TestProject.ServiceModel.Types.PocoObject' referenced from scope '', but it is not defined
If I change the Contains to a simpler equality comparison on another property, the AutoQuery works. Any ideas how to accomplish this?
You can't do a server side SQL query on a blobbed complex type property like List<string>. Any queries need to be applied on the client after the results are returned from the db and its deserialised back into a typed POCO.
Assume that I have Web API method:
[HttpGet]
public string SomeAction([FromUri] ObjectA a, [FromUri] ObjectB b)
{
return Ok("test");
}
How to call it using HttpClient? What is the best way to prepare appropriate query string for objects A and B?
UPDATE
ObjectA and ObjectB are complex objects... e.g.
public class ObjectA
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
public double Prop3 { get; set; }
}
I can prepare query string for every specific case, but I'm interested to get any universal method that would allow to work with any objects...
For now I see only one possible solution - using reflection go through the list of properties and build query string. I assume that there should be some already implemented mechanism... am I wrong?
If you're including complex objects on the query string, I will assume they are being encoded in some manner, from what you're trying to do, it would have to be a default encoding or you won't be able to put them on the query string.
Normal routing would be:
/{controller}/{action}/{a}/{b}
or
/{controller}/{action}?a={content}&b={content}
Dependent upon how you have your routes configured and your preference on using query string parameters as opposed to "friendly" urls.
Alternately, the MVC framework will attempt to populate your object from values present in any available data location (i.e. query string, cookies, et. al.) where variable names = property names. In this case, you could use something like this:
/SomeController/SomeAction?objectaProp1=abc&objectaProp2=def...
Hope this helps.
Havin a Response with a complex property, i want to to map to my responseDTO properly. For all basic types it works out flawlessly.
The ResponseDTO looks like this:
public class ResponseDto
{
public string Id {
get;
set;
}
public struct Refs
{
public Genre GenreDto {
get;
set;
}
public Location LocationDto {
get;
set;
}
}
public Refs References {
get;
set;
}
}
Genre and Location are both for now simple classes with simple properties (int/string)
public class GenreDto {
public string Id {
get;
set;
}
public string Name {
get;
set;
}
}
Question:
Is there any way, without changing/replacing the generic unserializer ( and more specific example) (in this example JSON ) to map such complex properties?
One specific difference to the GithubResponse example is, that i cant use a dictionry of one type, since i have different types under references. Thats why i use a struct, but this seems not to work. Maybe only IEnumerable are allowed?
Update
There is a way using lamda expressins to parse the json manually github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack.Text.Tests/UseCases/CentroidTests.cs#L136 but i would really like to avoid this, since the ResponseDTO becomes kinda useless this way - since when writing this kind of manual mapping i would no longer us Automapper to map from ResponseDto to DomainModel - i though like this abstraction and "seperation".
Thanks
I used lambda expressions to solve this issue, a more complex example would be
static public Func<JsonObject,Cart> fromJson = cart => new Cart(new CartDto {
Id = cart.Get<string>("id"),
SelectedDeliveryId = cart.Get<string>("selectedDeliveryId"),
SelectedPaymentId = cart.Get<string>("selectedPaymentId"),
Amount = cart.Get<float>("selectedPaymentId"),
AddressBilling = cart.Object("references").ArrayObjects("address_billing").FirstOrDefault().ConvertTo(AddressDto.fromJson),
AddressDelivery = cart.Object("references").ArrayObjects("address_delivery").FirstOrDefault().ConvertTo(AddressDto.fromJson),
AvailableShippingTypes = cart.Object("references").ArrayObjects("delivery").ConvertAll(ShippingTypeDto.fromJson),
AvailablePaypmentTypes = cart.Object("references").ArrayObjects("payment").ConvertAll(PaymentOptionDto.fromJson),
Tickets = cart.Object("references").ArrayObjects("ticket").ConvertAll(TicketDto.fromJson)
});
So this lamda exprpession is used to parse the JsonObject response of the request and map everything inside, even nested ressources. This works out very well and flexible
Some time ago i stumbled upon a similar problem. Actually ServiceStack works well with complex properties. The problem in my scenario was that i was fetching data from a database and was passing the objects returned from the DB provider directly to ServiceStack. The solution was to either create DTOs out of the models returned by the DB provider or invoke .ToList() on those same models.
I'm just sharing some experience with SS but may be you can specify what's not working for you. Is there an exception thrown or something else.
I am new to WebAPI and rest and am trying to do things correctly. By default if I were to access something such as User I would call api/user/5 if I wanted user 5. This would go to my User controller to Get(int num) I think. But I know I will often need other params passed as well. Currently I have Get(JObject data), but that data param is for other parameters. I will need other optional params whether I am sending an ID or wanting a list of everything. How do I go about organizing methods properly with WebAPI? Am I misunderstanding something?
To clarify:
This question is more about REST than dynamic objects, though they play a part:
How do I get a single resource vs a list of resources when I need additional params. I see those concepts as two separate methods, but the additional params complicate it in my mind when routing is involved.
Use attribute routing
For example -
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
or
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
if you need to return a list, create a method that returns a list, otherwise return the specific item requested
Look into using JToken or the even more dynamic 'dynamic' (Taken from here)
"
JSON and JavaScript is really dynamic, though, and often it's a hassle to try to "deserialize" really dynamic JSON objects into strongly-typed .NET structures. JSON.NET and ASP.NET Web API's model binding offer a happy medium - a middle ground - called JToken.
public class ContactController : ApiController
{
public JToken Post(JToken contact)
{
return contact;
}
}
Using JToken gives a dynamic container but also a DOM-like navigation model. But if that's not dynamic enough for me, why can't my method's parameter just take a "dynamic."
C# is statically typed, sure, but that doesn't mean I can't statically type something dynamic. ;)
Again, note the watch window.
Using dynamic to catch JSON post payloads
public class ContactController : ApiController
{
public dynamic Post(dynamic contact)
{
return contact;
}
}
"
I think you should make a new object for each WebAPI function that will handle the request. You can make the parameters optional with nullable properties.
[HttpPost]
public void SampleFunction(SampleFunctionModel model)
{
}
where SampleFunctionModel is:
public class SampleFunctionModel
{
public int? Id { get; set; }
public string Name { get; set; }
}