问题
I have a working (simplified) ODataController
with the following method.
public class MyTypeController : ODataController
{
[HttpGet]
[EnableQuery]
[ODataRoute("myTypes")]
public IQueryable<MyType> GetMyTypes(ODataQueryOptions<MyType> options)
{
return _repo.myResultsAsQueryable();
}
}
I would like to be able to call this method from the server and to do this I need to instantiate an ODataQueryOptions
which requires an ODataQueryContext
.
There are examples of how to do this (Eg. here and here) but they all seem to reference a previous version of OData. The ODataQueryContext constructor currently requires a third argument (ODataPath
path) which is not addressed in any examples that I can find.
Edit: @snow_FFFFFF, Here's some more context... I realize that I can simply consume the OData endpoint via a HttpClient but I would like to interact with the IQueryable directly as you say.
The problem is that the application I'm working on allows users to create filters (like a sophisticated search engine) that can be saved and later recalled by other users. From a JS client, they simply lookup the filter by id, and issue a query against the OData endpoint with the filter applied to the query string. This works very well from the client-side but I would like to be able to do something similar from the server-side as well.
This is what I would like to do but how can I instantiate the ODataPath argument?
public IQueryable<MyType> FilterMyTypes(int filterID)
{
// lookup filter by filterID from db...
filter = "$filter=Status eq 1"; // for example...
ODataPath path = // but how can I get the path!!!
new ODataQueryContext(edmModel, typeof(MyType), path);
var uri = new HttpRequestMessage(HttpMethod.Get, "http://localhost:56339/mytypes?" + filter);
var opts = new ODataQueryOptions<MyType>(ctx, uri);
var results = new MyTypeController().GetMyTypes(opts);
}
Another application of this would be to support dynamic grouping as below:
[HttpGet]
[Route("myTypes/{filterID:int}/groupby/{groupByFieldName}")]
public IHttpActionResult GroupMyTypes(int filterID, string groupByFieldName)
{
// For example: get all Active MyTypes and group by AssignedToUserID...
// Get the results of the filter as IQueryable...
var results = FilterMyTypes(filterID);
// group on groupByFieldName
var grouped = results.GroupBy(x => GetPropertyValue(x,groupByFieldName));
// select the groupByFieldName and the count
var transformedResults = grouped.Select(g => new { g.Key, Count = g.Count() });
return Ok(transformedResults);
}
回答1:
Sure. ODataPath is a list of ODataPathSegment(s) which should follow up the OData Uri spec.
In Web API OData, it's easy to instantiate an ODataPath, for example:
IEdmModel model = GetEdmModel();
IEdmEntitySet entitySet = model.EntityContainer.FindEntitySet(setName);
ODataPath path = new ODataPath(new EntitySetPathSegment(entitySet));
The above path
follows up the OData spec that it has the odata template as:
~/entityset
More test cases (codes) can be found here
回答2:
Your odata controller is providing an HTTP interface to your data, shouldn't you access it via HTTP (even if from the server)? There is a VS add-in to generate odata client code here:
https://visualstudiogallery.msdn.microsoft.com/9b786c0e-79d1-4a50-89a5-125e57475937
Or, if you are doing this from within the same project, why not a common method that returns the IQueryable that could be called from your code or from the controller?
UPDATE: Based on more information in the original question:
If you have ODataQueryOptions defined in a controller method, it will allow you to parse the well-formed odata query that is calling that method. I have used this when I needed to translate parts of an odata query because I needed to query multiple data sources to return the final result.
It sounds like you want something that takes non-odata parameters and options. For this, you probably need to look at custom Actions and or Functions (if you are just returning data, probably a function):
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/odata-actions-and-functions
UPDATE #2: And after more detailed reading, I think I missed your point - I don't have the answer, but I'll play around with it. Can't you just reform the URL itself (as opposed to instantiating the query options?
UPDATE #3: I think you are going to have a hard time tricking it into thinking it is getting an odata request...that isn't really an odata request. Back to the second option mentioned in my original answer - why not a common method that you can use and the odata controller can use - something like this:
// some sort of helper class
public class HelperObject
{
public static IQueryable<MyType> GetGroupedValues(int filterID, string groupByFieldName)
{
// all your code/logic here
}
}
// your odata controller uses the helper
[HttpGet]
[Route("myTypes/{filterID:int}/groupby/{groupByFieldName}")]
public IHttpActionResult GroupMyTypes(int filterID, string groupByFieldName)
{
return Ok(HelperObject.GetGroupedValues(filterID, groupByFieldName));
}
// ... and so does your other code that wants to do the same thing
var x = HelperObject.GetGroupedValues(filterID, groupByFieldName);
来源:https://stackoverflow.com/questions/33572659/how-to-instantiate-odataqueryoptions