it-swarm.com.de

ASP .NET MVC 4 WebApi: OData-Abfragen manuell bearbeiten

Ich habe einen Webdienst, der mit WebAPI von ASP .NET MVC 4 ..__ erstellt wurde. Ich weiß, dass die Ebene, über der WebAPI arbeitet, automatisch OData-Abfragen) verarbeitet (wie $filter, $top, $skip), aber was ist, wenn ich die Filterung selbst durchführen möchte?

Ich gebe nicht einfach Daten aus meiner Datenbank zurück , aber ich habe eine andere Ebene, die einige Eigenschaften hinzufügt, einige Konvertierungen vornimmt usw. Also alle Daten abzufragen, zu konvertieren und an die WebAPI-Klasse für die OData-Filterung zurückzugeben, ist nicht möglich gerade nicht gut genug Es ist natürlich furchtbar langsam und im Allgemeinen eine beschissene Idee.

Gibt es eine Möglichkeit, die OData-Abfrageparameter weiterzugeben von meinem WebAPI-Einstiegspunkt zu den Funktionen, die ich aufrufe, um die Daten abzurufen und zu konvertieren?

Zum Beispiel würde ein GET to /api/people?$skip=10&$top=10 auf dem Server aufrufen:

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

Und in 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;
}
53
frapontillo

Ich bin gerade über diesen alten Beitrag gestolpert und füge diese Antwort hinzu, da es jetzt sehr einfach ist, die OData-Abfragen selbst zu bearbeiten. Hier ist ein Beispiel:

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

Die Abfrage von der URL wird in einen LINQ-Ausdrucksbaum übersetzt, der dann gegen das IQueryable ausgeführt wird, das Ihre Operation zurückgibt. Sie können den Ausdruck analysieren und die Ergebnisse auf beliebige Weise bereitstellen. Der Nachteil ist, dass Sie IQueryable implementieren müssen, was nicht besonders einfach ist. Werfen Sie einen Blick auf diese Blogpost-Serie, wenn Sie interessiert sind: http://blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro. aspx . Es wird von WCF Data Services gesprochen, die von der Web-API verwendeten Filterausdrücke sind jedoch sehr ähnlich.

3

Eine Möglichkeit mit Web-API wäre der Umgang mit Kunden-Nachrichten http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

Schreiben Sie einen benutzerdefinierten Handler wie folgt:

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

Registrieren in global.asax.cs

Methode in der Anwendungsklasse: 

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

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

Ich habe so etwas mit WCF Data Services und asp.net mvc 3.5 gemacht, aber es war ein bisschen kludge. 

Der erste Schritt besteht darin, den Pfad neu zu schreiben, damit die Optionen Skip und Top nicht zweimal, einmal von Ihnen und einmal von der Laufzeit angewendet werden. 

Ich habe das mit einem HttpModule umgeschrieben. In Ihrer BeginRequest-Methode haben Sie folgenden Code:

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

Dann überprüfen Sie einfach die Abfragezeichenfolge und entnehmen Sie die benötigten Parameter. 

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

Dann teilen Sie die Filterzeichenfolge auf und parsen Sie sie in eine SQL-Anweisung oder in andere Db-Befehle. Ich habe in meinem Fall NHibernate benutzt. Ich konnte auch einschränken, welche Filterbefehle ich unterstützte, was die Sache einfacher machte. Ich habe zum Beispiel keine Gruppierungen gemacht. Meist nur die Vergleichsoperatoren.

Es gibt eine Liste von Filteroperatoren unter OData.org . Teilen Sie die Zeichenfolge mit "und" und "oder" in einzelne Klauseln auf. Teilen Sie jede Klausel durch ein Leerzeichen auf, und Sie sollten ein 3-Element-Array mit dem Eigenschaftennamen in [0], dem Operator in [1] und dem Wert in [2] erhalten.

Die Klauseln beziehen sich auf eine Person, aber ich gehe davon aus, dass Sie sie in etwas umwandeln können, das die richtigen Ergebnisse aus der Datenbank zieht. 

Ich habe diesen Ansatz aufgegeben, da er für POSTS nicht funktioniert. Ich musste meinen eigenen Linq-Provider schreiben, aber das Schreiben meines eigenen Filterstring-Parsers machte das verständlicher.

0
Jason Freitas