odatacontroller example delete custom rest odata asp.net-mvc-4 asp.net-web-api

rest - example - ASP.NET MVC 4 WebApi: maneje manualmente consultas de OData



odatacontroller (4)

Acabo de tropezar con esta publicación anterior y estoy agregando esta respuesta, ya que ahora es muy fácil manejar las consultas de OData usted mismo. Aquí hay un ejemplo:

[HttpGet] [ActionName("Example")] public IEnumerable<Poco> GetExample(ODataQueryOptions<Poco> queryOptions) { var data = new Poco[] { new Poco() { id = 1, name = "one", type = "a" }, new Poco() { id = 2, name = "two", type = "b" }, new Poco() { id = 3, name = "three", type = "c" } }; var t = new ODataValidationSettings() { MaxTop = 2 }; queryOptions.Validate(t); //this is the method to filter using the OData framework //var s = new ODataQuerySettings() { PageSize = 1 }; //var results = queryOptions.ApplyTo(data.AsQueryable(), s) as IEnumerable<Poco>; //or DIY var results = data; if (queryOptions.Skip != null) results = results.Skip(queryOptions.Skip.Value); if (queryOptions.Top != null) results = results.Take(queryOptions.Top.Value); return results; } public class Poco { public int id { get; set; } public string name { get; set; } public string type { get; set; } }

Tengo un servicio web hecho usando la WebAPI proporcionada por ASP .NET MVC 4. Sé que la capa sobre la cual trabaja WebAPI maneja automáticamente las consultas de OData (tales como $filter , $top , $skip ), pero que si quiero para manejar el filtrado por mi cuenta?

No solo devuelvo datos de mi base de datos , sino que tengo otra capa que agrega algunas propiedades, realiza algunas conversiones, etc. Por lo tanto, consultar TODOS mis datos, convertirlos y devolverlos a la clase WebAPI para el filtrado OData no es solo bueno. suficiente. Por supuesto, es terriblemente lento, y en general es una mala idea.

Entonces, ¿hay una manera de propagar los parámetros de consulta OData desde mi punto de entrada WebAPI a las funciones que llamo para obtener y convertir los datos?

Por ejemplo, un GET a /api/people?$skip=10&$top=10 llamaría al servidor:

public IQueryable<Person> get() { return PersonService.get(SomethingAboutCurrentRequest.CurrentOData); }

Y en PersonService :

public IQueryable<Person> getPeople(var ODataQueries) { IQueryable<ServerSidePerson> serverPeople = from p in dbContext.ServerSidePerson select p; // Make the OData queries // Skip serverPeople = serverPeople.Skip(ODataQueries.Skip); // Take serverPeople = serverPeople.Take(ODataQueries.Take); // And so on // ... // Then, convert them IQueryable<Person> people = Converter.convertPersonList(serverPeople); return people; }


Hice algo así con WCF Data Services y asp.net mvc 3.5, pero fue un poco complicado.

El primer paso es volver a escribir la ruta para que las opciones de salto y arriba no se apliquen dos veces, una vez por ti y una vez por el tiempo de ejecución.

Hice la reescritura con un HttpModule. En tu método BeginRequest tendrías un código como este:

HttpApplication app = (HttpApplication)sender; if (HttpContext.Current.Request.Path.Contains(YOUR_SVC)) { if (app.Request.Url.Query.Length > 0) { //skip questionmark string queryString = app.Request.Url.Query.Substring(1) .Replace("$filter=", "filter=") .Replace("$orderby=", "orderby=") .Replace("$top=", "top=") .Replace("$skip=", "skip="); HttpContext.Current.RewritePath(app.Request.Path, "", queryString); } }

Luego solo examine la cadena de consulta y elimine los parámetros que necesita.

if (HttpContext.Current.Request.QueryString["filter"] != null) var filter = HttpContext.Current.Request.QueryString["filter"] as string;

Luego divida la cadena de filtro y analícela en una instrucción sql o cualquier otro comando db. Estaba usando NHibernate en mi caso. También pude limitar qué comandos de filtro admití, lo que hizo las cosas más fáciles. No hice agrupaciones, por ejemplo. Principalmente solo los operadores de comparación.

Hay una lista de operadores de filtros en OData.org . Divida la cadena por "y" y "o" en cláusulas individuales. Divida cada cláusula por un espacio y debería obtener una matriz de 3 elementos con el nombre de la propiedad en [0] el operador en [1] y el valor en [2].

Las cláusulas serán en términos de una Persona, pero supongo que podrá convertirlas en algo que saque los resultados correctos de la base de datos.

Terminé abandonando este enfoque, ya que no funcionará para POSTS. Tuve que escribir mi propio proveedor de Linq, pero escribir mi propio analizador de cadenas de filtros lo hizo más fácil de entender.


La consulta de la URL se traduce en un árbol de expresiones LINQ que luego se ejecuta contra IQueryable que devuelve su operación. Puede analizar la expresión y proporcionar los resultados de la forma que desee. La desventaja es que debe implementar IQueryable, que no es muy fácil. Eche un vistazo a esta publicación de blog si está interesado: http://blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx . Habla sobre los servicios de datos WCF, pero las expresiones de filtro utilizadas por la API web serán muy similares.


Una forma con Web-api sería con el manejador de mensajes del cliente http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

Escriba un controlador personalizado como a continuación:

public class CustomHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken).ContinueWith( (task) => { HttpResponseMessage response = task.Result; var persons = response.Content.ReadAsAsync<IQueryable<Person>>().Result; var persons2 = new List<Person>(); //This can be the modified model completely different foreach (var item in persons) { item.Name = "changed"; // here you can change the data //persons2.Add(....); //Depending on the results modify this custom model } //overwrite the response response = new HttpResponseMessage<IEnumerable<Person>>(persons2); return response; } ); } }

Registrarse en global.asax.cs

Método en la clase de aplicación:

static void Configure(HttpConfiguration config) { config.MessageHandlers.Add(new CustomHandler()); } protected void Application_Start() { .... ..... //call the configure method Configure(GlobalConfiguration.Configuration); }