2016-04-20 15 views
6

Próbuję zbudować dostawcę LINQ do dobrze zdefiniowanego interfejsu API sieci Web z dobrze zdefiniowanym modelem. Śledzę te instruktaże:Usuwanie warunku z wyrażenia za pomocą ExpressionVisitor

udało mi się stworzyć Provider kwerendę, która przekłada się na żądany wyraz URL i działa całkiem dobrze.

Oto kolejny krok, którego nie mogę pojąć. Wyobraźmy sobie, że jeden z wniosków zwraca obiekt zdefiniowany następująco:

[JsonObject] 
public class SomeObject 
{ 
    [JsonProperty(PropertyName = "id")] 
    public string Id {get;set;} 
    [JsonProperty(PropertyName = "name")] 
    public string Name {get;set;} 
    [JsonProperty(PropertyName = "is_active")] 
    public bool IsActive {get;set;} 

    public string SomeOtherObjectId {get;set;} 
} 

Teraz jak widać SomeOtherObjectId nie jest ozdobiony atrybutu JsonProperty, to dlatego, że nie jest częścią zwróconej definicji obiektu, ale jest potrzebne, ponieważ jest to parametr, od którego zależy żądanie pobierania. Oznacza to, że wyraz tak:

Expression<Func<SomeObject, bool>> exp = o => o.SomeOtherObjectId == "someId" && o.IsActive; 

przekłada się na coś takiego:

blablabla/someId/....

Teraz wyszukiwania Web API są ograniczone parametry, których oczekują, więc filtr IsActive zostanie zignorowany, więc uznałem, że po otrzymaniu odpowiedzi mogę użyć tego samego wyrażenia co predykat w kwerendzie LINQ do Objects, więc pozostałe filtry są brane pod uwagę, ale w celu wykonania więc musiałbym odtworzyć wyraz bez filtra ProjectID ponieważ nie istnieje w zwróconym JSON, który pobiera rozszeregować do obiektu, więc będzie musiała stać się coś takiego:

Expression<Func<SomeObject, bool>> modifiedExp = o => o.IsActive; 

Więc mogę zrobić coś takiego

return deserializedObject.Where(modifiedExp); 

Szukałem przykładów tego, jak to zrobić za pomocą ExpressionVisitor, ale po prostu nie mogę zrozumieć, jak odtworzyć wyrażenie bez filtra, który chcę usunąć.

Dziękuję za pomoc.

UPDATE:

Dziękuję zarówno Evk i MaKCbIMKo ponieważ z ich łączna odpowiedzi dostałem ostateczne rozwiązanie, które mam w tym w nadziei, że może pomóc innym.

protected override Expression VisitBinary(BinaryExpression node) 
    { 
     if (node.NodeType != ExpressionType.Equal) return base.VisitBinary(node); 

     if (new[] { node.Left, node.Right } 
      .Select(child => child as MemberExpression) 
      .Any(memberEx => memberEx != null && memberEx.Member.CustomAttributes.All(p => p.AttributeType.Name != "JsonPropertyAttribute"))) 
     { 
      return Expression.Constant(true); 
     } 

     return base.VisitBinary(node); 
    } 
+0

Musisz wiedzieć, jak to zrobić w ogólnym przypadku lub po prostu dla konkretnego wyrażenia, które podałeś (o.SomeOtherObjectId == "someId" && o.IsActive)? – Evk

+0

@Evk - W tym przypadku, jeśli parametr ParameterName == "SomeOtherObjectId" powinien zostać usunięty z istniejącego wyrażenia, pozostawiając resztę bez zmian, niezależnie od tego, jak będzie wyglądać wyrażenie. Nie jestem pewien, czy to odpowiedź na twoje pytanie. –

Odpowiedz

2

To zależy od tego, jak skomplikowane drzewo wyrażenie jest naprawdę, ale w najprostszym przypadku można zrobić tak:

class Visitor : ExpressionVisitor { 
    protected override Expression VisitBinary(BinaryExpression node) { 
     // SomeOtherObjectId == "someId" 
     if (node.NodeType == ExpressionType.Equal) { 
      foreach (var child in new[] {node.Left, node.Right}) { 
       var memberEx = child as MemberExpression; 
       if (memberEx != null && memberEx.Member.Name == "SomeOtherObjectId") { 
        // return True constant 
        return Expression.Constant(true); 
       } 
      } 
     } 
     return base.VisitBinary(node); 
    }   
} 

ta zakłada swoje „SomeOtherObjectId ==«someId»” ekspresja jest pewne „i "łańcuch, więc po prostu zamień go na" true ", który usuwa jego efekt.

Następnie zrobić:

var anotherExp = (Expression<Func<SomeObject, bool>>) new Visitor().Visit(exp); 

Jeśli wyrażenie może być bardziej skomplikowane - ten przykład powinien dać wyobrażenie o tym, jak go obsłużyć.

+0

To było dokładnie to, czego szukałem, brakowało mi między innymi "zwrotu Expression.Constant (true)". Dziękuję Ci. –

2

Oto basic example o modyfikacji drzewa ekspresji.

Będziesz musiał stworzyć własnego ExpressionVisitor i skonfigurować go tak, aby wyszukiwał warunków, które używają właściwości, które chcesz pominąć (np. Brakuje atrybutu Json lub innego atrybutu).

Następnie wystarczy usunąć je z drzewa lub zastąpić wyrażeniem "zawsze prawdziwe" zamiast sprawdzać wartość właściwości.

Ponadto, here możesz znaleźć przydatne informacje o tym, o co prosisz.

+0

@MaKCblMKo - Niestety, widziałem już wspomniane referencje, ale nadal brakowało Ci kilku rzeczy, aby uzyskać rozwiązanie. +1 za udzielenie mi pomysłu sprawdzenia atrybutu Json zamiast nazwy atrybutu, co powoduje, że jest to lepsze, bardziej ogólne rozwiązanie. –