こんにちは私は、たとえば、C#でOData $ filter文字列を解析するための最良のアプローチは何であるか疑問に思っています
/ API/organization?$ filter = "name eq'Facebook 'またはnameeq'Twitter'およびサブスクライバーgt'30 '"
FacebookまたはTwitterの名前を持ち、30人以上のサブスクライバーを持つすべての組織を返す必要があります。私はかなり研究しましたが、WCFを中心に展開しないソリューションを見つけることができません。正規表現を使用してグループ化することを考えていたので、次のようなFilterクラスのリストがあります。
Filter
Resource: Name
Operator: Eq
Value: Facebook
Filter
Resource: Name
Operator: Eq
Value: Twitter
Filter
Resource: Subscribers
Operator: gt
Value: 30
しかし、私はAND/ORの処理方法に困惑しています。
.NETには、これを行うためのライブラリがあります。独自の正規表現を作成すると、エッジケースを見逃すリスクがあります。
NuGetを使用して、Microsoft.Data.ODataを取り込みます。次に、次のことができます。
using Microsoft.Data.OData.Query;
var result = ODataUriParser.ParseFilter(
"name eq 'Facebook' or name eq 'Twitter' and subscribers gt 30",
model,
type);
ここでのresult
は、フィルター句を表すASTの形式になります。
(model
およびtype
入力を取得するには、次のようなものを使用して$ metadataファイルを解析できます。
using Microsoft.Data.Edm;
using Microsoft.Data.Edm.Csdl;
IEdmModel model = EdmxReader.Parse(new XmlTextReader(/*stream of your $metadata file*/));
IEdmEntityType type = model.FindType("organisation");
)
Jen Sの発言に基づいて、FilterClauseによって返されるASTツリーをトラバースできます。
たとえば、コントローラーのクエリオプションからFilterClauseを取得できます。
public IQueryable<ModelObject> GetModelObjects(ODataQueryOptions<ModelObject> queryOptions)
{
var filterClause = queryOptions.Filter.FilterClause;
次に、結果のASTツリーを次のようなコードでトラバースできます( この記事 から借用):
var values = new Dictionary<string, object>();
TryNodeValue(queryOptions.Filter.FilterClause.Expression, values);
呼び出される関数は次のようになります。
public void TryNodeValue(SingleValueNode node, IDictionary<string, object> values)
{
if (node is BinaryOperatorNode )
{
var bon = (BinaryOperatorNode)node;
var left = bon.Left;
var right = bon.Right;
if (left is ConvertNode)
{
var convLeft = ((ConvertNode)left).Source;
if (convLeft is SingleValuePropertyAccessNode && right is ConstantNode)
ProcessConvertNode((SingleValuePropertyAccessNode)convLeft, right, bon.OperatorKind, values);
else
TryNodeValue(((ConvertNode)left).Source, values);
}
if (left is BinaryOperatorNode)
{
TryNodeValue(left, values);
}
if (right is BinaryOperatorNode)
{
TryNodeValue(right, values);
}
if (right is ConvertNode)
{
TryNodeValue(((ConvertNode)right).Source, values);
}
if (left is SingleValuePropertyAccessNode && right is ConstantNode)
{
ProcessConvertNode((SingleValuePropertyAccessNode)left, right, bon.OperatorKind, values);
}
}
}
public void ProcessConvertNode(SingleValuePropertyAccessNode left, SingleValueNode right, BinaryOperatorKind opKind, IDictionary<string, object> values)
{
if (left is SingleValuePropertyAccessNode && right is ConstantNode)
{
var p = (SingleValuePropertyAccessNode)left;
if (opKind == BinaryOperatorKind.Equal)
{
var value = ((ConstantNode)right).Value;
values.Add(p.Property.Name, value);
}
}
}
次に、リストディクショナリを調べて、値を取得できます。
if (values != null && values.Count() > 0)
{
// iterate through the filters and assign variables as required
foreach (var kvp in values)
{
switch (kvp.Key.ToUpper())
{
case "COL1":
col1 = kvp.Value.ToString();
break;
case "COL2":
col2 = kvp.Value.ToString();
break;
case "COL3":
col3 = Convert.ToInt32(kvp.Value);
break;
default: break;
}
}
}
この例は、「eq」評価のみを説明するという点でかなり単純ですが、私の目的ではうまく機能しました。 YMMV。 ;)
私はあなたがビジターパターンを使用して提供されたインターフェースでASTをトラバースすることになっていると思います。
フィルタを表すこのクラスがあると考えてください
public class FilterValue
{
public string ComparisonOperator { get; set; }
public string Value { get; set; }
public string FieldName { get; set; }
public string LogicalOperator { get; set; }
}
では、ODataパラメーターに付属するフィルターをクラスに「抽出」するにはどうすればよいでしょうか。
FilterClauseオブジェクトには、QueryNodeから継承するSingleValueNodeであるExpressionプロパティがあります。 QueryNodeには、QueryNodeVisitorを受け取るAcceptメソッドがあります。
public virtual T Accept<T>(QueryNodeVisitor<T> visitor);
そうです、あなたはあなた自身のQueryNodeVisitorを実装し、あなたの仕事をしなければなりません。以下は未完成の例です(私はすべての可能な訪問者を上書きしません)。
public class MyVisitor<TSource> : QueryNodeVisitor<TSource>
where TSource: class
{
List<FilterValue> filterValueList = new List<FilterValue>();
FilterValue current = new FilterValue();
public override TSource Visit(BinaryOperatorNode nodeIn)
{
if(nodeIn.OperatorKind == Microsoft.Data.OData.Query.BinaryOperatorKind.And
|| nodeIn.OperatorKind == Microsoft.Data.OData.Query.BinaryOperatorKind.Or)
{
current.LogicalOperator = nodeIn.OperatorKind.ToString();
}
else
{
current.ComparisonOperator = nodeIn.OperatorKind.ToString();
}
nodeIn.Right.Accept(this);
nodeIn.Left.Accept(this);
return null;
}
public override TSource Visit(SingleValuePropertyAccessNode nodeIn)
{
current.FieldName = nodeIn.Property.Name;
//We are finished, add current to collection.
filterValueList.Add(current);
//Reset current
current = new FilterValue();
return null;
}
public override TSource Visit(ConstantNode nodeIn)
{
current.Value = nodeIn.LiteralText;
return null;
}
}
次に、発射します:)
MyVisitor<object> visitor = new MyVisitor<object>();
options.Filter.FilterClause.Expression.Accept(visitor);
それが木を横断したときあなたの
visitor.filterValueList
希望する形式のフィルターが含まれている必要があります。もっと作業が必要だと思いますが、これを実行できれば、理解できると思います。