9

Ich möchte in der Lage sein, den Filter im Controller zu ändern und dann die Daten basierend auf dem geänderten Filter zurückgeben.

Also ich habe einen ODataQueryOptions-Parameter auf der Serverseite, die ich verwenden kann, um die FilterQueryOption zu betrachten.

Nehmen wir an, der Filter ist ungefähr so ​​"$ filter = ID eq -1", aber auf der Serverseite, wenn ich "-1" für eine ID sehe, sagt dieser, dass der Benutzer alle Datensätze auswählen möchte.

Ich habe versucht, die "$ filter = ID eq-1" in "$ filter = ID ne-1" zu ändern, die mir alle durch die Einstellung der Filter.RawValue geben würde, aber das ist nur lesbar.
Ich habe versucht, eine neue FilterQueryOption zu erstellen, aber dies erfordert einen ODataQueryContext und einen ODataQueryOptionParser, von denen ich nicht weiß, wie ich sie erstellen soll.

Ich habe dann versucht, den Filter = Null und dann uns die ApplyTo, die scheint zu arbeiten, wenn ich einen Unterbrechungspunkt in der Steuerung und überprüfen Sie dies auf der unmittelbaren Fenster, aber sobald es die GET-Methode auf dem Controller dann es verlässt "kehrt" zurück zu dem, was in der URL übergeben wurde.

Dieser Artikel spricht davon, etwas sehr ähnliches "The best way to modify a WebAPI OData QueryOptions.Filter" zu tun, aber sobald es die GET-Methode des Controllers verlässt, wird es wieder auf den URL-Abfragefilter zurückgesetzt.

UPDATE MIT SAMPLE CODE

[EnableQuery] 
[HttpGet] 
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions) 
{ 
    if (queryOptions.Filter != null) 
    { 
     var url = queryOptions.Request.RequestUri.AbsoluteUri; 
     string filter = queryOptions.Filter.RawValue; 

     url = url.Replace("$filter=ID%20eq%201", "$filter=ID%20eq%202"); 
     var req = new HttpRequestMessage(HttpMethod.Get, url); 

     queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req); 
    } 

    IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable()); 
    return query as IQueryable<Product>; 
} 

diesen Code ausführen, werden keine Produkte zurückgeben dies, weil die ursprüngliche Abfrage in der URL-Produkt 1 und tauschte ich die ID-Filter des Produkts 1 mit Produkt 2 wollte.
Jetzt, wenn ich SQL Profiler ausführen, kann ich sehen, dass es etwas wie "Select * aus Produkt WHERE ID = 1 UND ID = 2" hinzugefügt.

ABER Wenn ich das gleiche tue, indem ich die $ Spitze ersetze, dann funktioniert es gut.

[EnableQuery] 
[HttpGet] 
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions) 
{ 
    if (queryOptions.Top != null) 
    { 
     var url = queryOptions.Request.RequestUri.AbsoluteUri; 
     string filter = queryOptions.Top.RawValue; 

     url = url.Replace("$top=2", "$top=1"); 
     var req = new HttpRequestMessage(HttpMethod.Get, url); 

     queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req); 
    } 

    IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable()); 
    return query as IQueryable<Product>; 
} 

END RESULT
Mit Hilfe von Microsoft. Hier ist die endgültige Ausgabe, die Filter, Count und Paging unterstützt.

using System.Net.Http; 
using System.Web.OData; 
using System.Web.OData.Extensions; 
using System.Web.OData.Query; 

/// <summary> 
/// Used to create custom filters, selects, groupings, ordering, etc... 
/// </summary> 
public class CustomEnableQueryAttribute : EnableQueryAttribute 
{ 
    public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions) 
    { 
     IQueryable result = default(IQueryable); 

     // get the original request before the alterations 
     HttpRequestMessage originalRequest = queryOptions.Request; 

     // get the original URL before the alterations 
     string url = originalRequest.RequestUri.AbsoluteUri; 

     // rebuild the URL if it contains a specific filter for "ID = 0" to select all records 
     if (queryOptions.Filter != null && url.Contains("$filter=ID%20eq%200")) 
     { 
      // apply the new filter 
      url = url.Replace("$filter=ID%20eq%200", "$filter=ID%20ne%200"); 

      // build a new request for the filter 
      HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url); 

      // reset the query options with the new request 
      queryOptions = new ODataQueryOptions(queryOptions.Context, req); 
     } 

     // set a top filter if one was not supplied 
     if (queryOptions.Top == null) 
     { 
      // apply the query options with the new top filter 
      result = queryOptions.ApplyTo(queryable, new ODataQuerySettings { PageSize = 100 }); 
     } 
     else 
     { 
      // apply any pending information that was not previously applied 
      result = queryOptions.ApplyTo(queryable); 
     } 

     // add the NextLink if one exists 
     if (queryOptions.Request.ODataProperties().NextLink != null) 
     { 
      originalRequest.ODataProperties().NextLink = queryOptions.Request.ODataProperties().NextLink; 
     } 
     // add the TotalCount if one exists 
     if (queryOptions.Request.ODataProperties().TotalCount != null) 
     { 
      originalRequest.ODataProperties().TotalCount = queryOptions.Request.ODataProperties().TotalCount; 
     } 

     // return all results 
     return result; 
    } 
} 

Antwort

5

entfernen [EnableQuery] Attribut, sollte Ihr Szenario arbeiten, denn nach diesem Attribute einer OData/WebAPI Ihre ursprüngliche Abfrage Option anwenden, nachdem Sie Daten in Controller zurückgeben, wenn Sie bereits in Ihrer Controller-Methode anwenden, dann Sie sollten dieses Attribut nicht verwenden.

Aber wenn Ihre Abfrage Option wählen $ enthält, werden diejenigen Code nicht, weil der Typ des Ergebnis nicht funktioniert Produkt ist, verwenden wir einen Wrapper das Ergebnis von $ wählen, darzustellen, so empfehle ich Ihnen diese verwenden versuchen:

Machen Sie eine maßgeschneiderte EnableQueryAttribute

public class MyEnableQueryAttribute : EnableQueryAttribute 
{ 
    public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions) 
    { 
     if (queryOptions.Filter != null) 
     { 
      queryOptions.ApplyTo(queryable); 
      var url = queryOptions.Request.RequestUri.AbsoluteUri; 

      url = url.Replace("$filter=Id%20eq%201", "$filter=Id%20eq%202"); 
      var req = new HttpRequestMessage(HttpMethod.Get, url); 

      queryOptions = new ODataQueryOptions(queryOptions.Context, req); 
     } 

     return queryOptions.ApplyTo(queryable); 
    } 
} 

Verwenden Sie dieses Attribut in Ihrem Controller-Methode

[MyEnableQueryAttribute] 
public IHttpActionResult Get() 
{ 
    return Ok(_products); 
} 

Hoffe, das kann Ihr Problem lösen, danke!

Ventilator.

+0

Das funktionierte fast. Es hat tatsächlich den $ filter -Befehl auf dem Server geändert, aber dann hat es auch den $ select -Befehl abgebrochen. Ich kann $ select nicht mehr verwenden, wenn ein Filter angewendet wird. Ich erhalte den Fehler "Die EDM-Instanz vom Typ 'Product Nullable = True' fehlt die Eigenschaft 'X'" Wobei 'X' der Eigenschaftsname ist, den ich auswählen wollte. Wenn ich alle Eigenschaften in $ select einfüge, scheint es gut zu funktionieren. – goroth

+0

Das funktioniert jetzt sogar mit $ select, nachdem Sie "ApplyTo" hinzugefügt haben, bevor die Abfrageoptionen neu initialisiert werden. Vielen Dank. – goroth

+0

Lösung funktioniert bei OData 5.5.1 nicht. Die Änderung der URL hat keine Auswirkungen. –

2

Als Antwort von @ Chris Schaller ich meine eigene Lösung wie unten schreiben:

public class CustomEnableQueryAttribute : EnableQueryAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     var url = actionContext.Request.RequestUri.OriginalString; 

     //change something in original url, 
     //for example change all A charaters to B charaters, 
     //consider decoding url using WebUtility.UrlDecode() if necessary 
     var newUrl = ModifyUrl(url); 

     actionContext.Request.RequestUri = new Uri(newUrl); 
     base.OnActionExecuting(actionContext); 
    } 
} 
+1

Vielen Dank, Ihre Lösung macht nichts mit den ODataQueryOptions zu tun, in denen sich viele meiner Probleme seit ODataLib v6 manifestiert haben. Wenn dies funktioniert, bearbeite ich Ihre Lösung mit einem Beispiel für "ModifyUrl", das normalerweise ODataQueryOptions ändern würde. –

+0

Nochmals vielen Dank @afsharm Ich denke, in den meisten Fällen ist OnActionExecuting eine bessere Position zum Erzwingen oder Ändern von OData $ -Filterparametern. Dies wird vor den meisten OData Query-Analysen ausgeführt, sodass Sie nicht mit dem Erstellen eines neuen ODataQueryOptions-Objekts mit einer gefälschten Anforderung herumhantieren müssen. Enttäuscht, dass ich das nicht viel früher herausgefunden habe. –

+0

@ Afshar - Danke für Ihre Lösung. Ich bin neu bei OData. Ich habe Ihre Lösung verfolgt, und ich habe die URL in OnActionExecuting geändert, wie Sie es getan haben. Aber innerhalb des Controllers, wenn ich den Wert für queryOptions.Filter.RawValue sehe, zeigt es alte Filterwerte. Aber die requeryOptions.Request.RequestUri zeigt die neu geänderte URL. Kannst du helfen? – manojmore