54 votes

ASP .NET MVC 4 WebApi: Gérer manuellement les requêtes OData

J'ai un Web Service effectuée à l'aide de la WebAPI fournis par l'ASP .NET MVC 4. Je sais que le couche sur le dessus de qui WebAPI œuvres gère automatiquement OData Requêtes (comme $filter, $top, $skip), mais que si je veux gérer le filtrage par moi-même?

Je n'ai pas simplement renvoyer des données de ma base de données, mais j'ai une autre couche qui ajoute certaines propriétés, en fait les conversions etc. Donc, l'interrogation de l'ENSEMBLE de mes données, de les convertir et de les retourner à la WebAPI classe pour OData le filtrage n'est pas juste assez bon. Bien sûr, c'est terriblement lent, et généralement une idée de merde.

Donc, il y a un moyen de propager la requête OData paramètres de mon WebAPI point d'entrée pour les fonctions que j'appelle pour obtenir et convertir les données?

Par exemple, vous rendre à /api/people?$skip=10&$top=10 pourrait appeler sur le serveur:

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

Et dans 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;
}

41voto

qujck Points 5994

Je viens de tomber par hasard sur cet ancien post et j'ajoute cette réponse car il est maintenant très facile de gérer les requêtes OData vous-même. Voici un exemple:

 [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; }
}
 

3voto

Vitek Karas MSFT Points 9879

La requête de l'URL est traduit en une expression LINQ l'arbre, qui est alors exécutée sur le IQueryable votre opération retourne. Vous pouvez analyser l'expression et de fournir les résultats dans la manière que vous voulez. L'inconvénient est que vous devez mettre en œuvre IQueryable qui n'est pas super facile. Jetez un oeil à ce blog de la série si vous êtes intéressé: http://blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx. Il parle des Services de Données WCF, mais les expressions de filtre utilisé par l'API Web sera très similaire.

2voto

Abhijit Kadam Points 1614

Une façon de procéder avec Web-api serait d'utiliser un gestionnaire de messages client http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

Écrivez un gestionnaire personnalisé comme ci-dessous:

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

Inscrivez-vous à global.asax.cs

Méthode en classe d'application:

 static void Configure(HttpConfiguration config)
 {
     config.MessageHandlers.Add(new CustomHandler()); 
 }

protected void Application_Start()
{
     ....
     .....
     //call the configure method
     Configure(GlobalConfiguration.Configuration);
 }
 

0voto

Jason Freitas Points 1428

J'ai fait quelque chose comme ça avec les Services de Données WCF et asp.net mvc 3.5 mais c'était un peu de bidouille.

La première étape consiste à réécrire le chemin de sorte que les ignorer et de meilleures options ne sont pas appliquées deux fois, une fois par vous et une fois par le runtime.

J'ai fait de la réécriture avec un HttpModule. Dans votre BeginRequest méthode que vous auriez un code comme ceci:

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

Puis il suffit d'examiner la chaîne de requête et arracher les paramètres dont vous avez besoin.

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

Diviser ensuite la chaîne de filtre et de l'analyser dans une instruction sql ou tout autre db commandes. J'ai été en utilisant NHibernate dans mon cas. J'ai aussi été en mesure de limiter les commandes de filtre j'ai soutenu ce qui rendait les choses plus faciles. Je ne voulais pas faire des regroupements par exemple. La plupart du temps simplement les opérateurs de comparaison.

Il y a une liste d'opérateurs de filtre à OData.org. Diviser la chaîne en "et" et "ou" dans les clauses particulières. Diviser chaque clause par un espace et vous devriez obtenir un 3 élément de tableau avec le nom de la propriété dans [0] l'opérateur dans [1] et la valeur dans [2].

Les clauses d'une Personne, mais je suis en supposant que vous serez en mesure de les convertir en quelque chose qui va tirer les bons résultats de la db.

J'ai fini par abandonner cette approche, car il ne fonctionnera pas pour les MESSAGES. J'ai eu à écrire mon propre fournisseur Linq mais l'écriture de mon propre chaîne de filtre analyseur fait que plus facile à comprendre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X