嗨,我想知道在C#中解析OData $过滤器字符串的最佳方法是什么
/ API/organizations?$ filter ="name eq'Facebook'或name eq'Twitter'和subscriber gt'30''
应该返回所有名称为Facebook或Twitter且拥有超过30个订阅者的组织.我已经研究了很多,但找不到任何不围绕WCF的解决方案.我正在考虑使用Regex并对它们进行分组,因此我有一个Filter类列表,这样:
Filter
Resource: Name
Operator: Eq
Value: Facebook
Filter
Resource: Name
Operator: Eq
Value: Twitter
Filter
Resource: Subscribers
Operator: gt
Value: 30
Run Code Online (Sandbox Code Playgroud)
但我很难过如何处理ANDs/ORs.
Jen*_*n S 16
在.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);
Run Code Online (Sandbox Code Playgroud)
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");
Run Code Online (Sandbox Code Playgroud)
)
小智 13
在Jen S所说的基础上,您可以遍历FilterClause返回的AST树.
例如,您可以从控制器的查询选项中检索FilterClause:
public IQueryable<ModelObject> GetModelObjects(ODataQueryOptions<ModelObject> queryOptions)
{
var filterClause = queryOptions.Filter.FilterClause;
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用以下代码遍历生成的AST树(从本文中借用):
var values = new Dictionary<string, object>();
TryNodeValue(queryOptions.Filter.FilterClause.Expression, values);
Run Code Online (Sandbox Code Playgroud)
调用的函数是这样的:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以浏览列表字典并检索您的值:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这个例子相当简单,因为它只考虑"eq"评估,但就我的目的而言,它运作良好.因人而异.;)
PvP*_*ten 12
我认为你应该使用访问者模式提供的接口来运行AST.
考虑您有这个代表过滤器的类
public class FilterValue
{
public string ComparisonOperator { get; set; }
public string Value { get; set; }
public string FieldName { get; set; }
public string LogicalOperator { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
那么,我们如何"提取"OData参数附带的过滤器到您的类?
那么FilterClause对象有一个Expression属性,它是一个继承自QueryNode的SingleValueNode.QueryNode具有接受QueryNodeVisitor的Accept方法.
public virtual T Accept<T>(QueryNodeVisitor<T> visitor);
Run Code Online (Sandbox Code Playgroud)
是的,所以你必须实现自己的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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,开火:)
MyVisitor<object> visitor = new MyVisitor<object>();
options.Filter.FilterClause.Expression.Accept(visitor);
Run Code Online (Sandbox Code Playgroud)
当它遍历了你的树
visitor.filterValueList
Run Code Online (Sandbox Code Playgroud)
应包含所需格式的过滤器.我确定需要做更多的工作,但如果你能够实现这一目标,我想你可以搞清楚.