我需要对来自外部 REST API 的数据进行列表/获取/更新/创建/销毁(即执行 CRUD 事件)。
此 API 有一个自定义过滤器语法,如下所示:
{{BaseUrl}}/V1.0/<Entity>/query?search={"filter":[{"op":"eq","field":"id","value":"68275"}]}
这个过滤器语法非常灵活,基本上允许你做fieldA == x
AND/OR
fieldB != y
查询。
id <= 1000 && Title == "Some title"
{
"filter": [
{
"op": "le",
"field": "id",
"value": 1000
},
{
"op": "eq",
"field": "Title",
"value": "Some title"
}
]
}
firstname == "john" || lastname != "Jones"
{
"filter": [
{
"op": "or",
"items": [
{
"op": "eq",
"field": "firstname",
"value": "John"
},
{
"op": "ne",
"field": "lastname",
"value": "Jones"
}
]
}
]
}
如果您好奇,这是自动任务 API:https://ww3.autotask.net/help/DeveloperHelp/Content/APIs/REST/General_Topics/REST_Swagger_UI.htm
目前,我有一些类可以转换为第一个示例查询 id <= 1000 && Title == "Some title"
.
public interface IAutotaskFilter
{
string Field { get; }
string Value { get; }
string ComparisonOperator { get; }
}
public interface IAutotaskQuery
{
void AddFilter(IAutotaskFilter autotaskFilter);
void AddFilters(IList<IAutotaskFilter> filters);
void RemoveFilter(IAutotaskFilter autotaskFilter);
}
问题是这违反了我所拥有的整洁架构。
Clean Architecture Example from Microsoft
如果我使用这些类(通过上述接口(interface)),那么我的应用程序层将依赖于我的基础设施层的实现细节。换句话说,我的业务逻辑将知道如何为这个特定的外部 API 构造查询。
据我所知,我有几个选择:
实现一个自定义 LINQ 提供程序,将 linq 查询转换为此语法。据我所知,这是非常困难的。我没有找到任何这方面的最新信息。任何尝试过的图书馆至少已经有 7 年没有被触及过。如果可能的话,我很乐意这样做。拥有流畅的查询语法对我来说非常有吸引力。
收起并按原样使用。
在本地缓存 API 实体,并使用其 Webhook“更新”/“创建”/“销毁”事件进行缓存失效。这需要大量的处理、数据传输和复杂性,因此可能不值得。
我希望有更好的选择,但我还没有找到。请告诉我。
如果有帮助,自动任务 API 会提供 OpenAPI v1 文档。
最佳答案
查看 system.linq.expressions
命名空间中的 Expression
类和 ExpressionVisitor
https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions?view=net-5.0
它是LINQ的本质,但没有SQL数据库部分。它允许您用 C# 编写表达式并将其转换为任何格式。请参阅下面的代码示例。
通过分配 lambda 表达式,如 (bo) => bo.MyID == 1 || bo.MyID == 3
到 Expression 类型,原始表达式在代码中可用。
这是 C# 编译器的一项功能,LINQ 在将查询编译为字节代码之前使用它来捕获查询。
更改下面代码的 TextWriter 部分,以按照 API 要求的格式写入字符串。
class BObject
{
public int MyID;
}
class MyExpressionVisitor : ExpressionVisitor
{
private TextWriter writer;
public MyExpressionVisitor(TextWriter writer)
{
this.writer = writer;
}
protected override Expression VisitBinary(BinaryExpression node)
{
writer.WriteLine("Binary node " + node.NodeType);
return base.VisitBinary(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
writer.WriteLine("Constant node " + node.NodeType + " Value " + node.Value);
return base.VisitConstant(node);
}
protected override Expression VisitMember(MemberExpression node)
{
writer.WriteLine("Constant node " + node.NodeType + " Member " + node.Member);
return base.VisitMember(node);
}
}
class Program
{
static void Main(string[] args)
{
Expression<Func<BObject, bool>> query = (bo) => bo.MyID == 1 || bo.MyID == 3;
var visitor = new MyExpressionVisitor(Console.Out);
visitor.Visit(query.Body);
Console.ReadKey();
}
}
输出:
Binary node OrElse
Binary node Equal
Constant node MemberAccess Member Int32 MyID
Constant node Constant Value 1
Binary node Equal
Constant node MemberAccess Member Int32 MyID
Constant node Constant Value 3
关于c# - 使用具有复杂过滤器语法的外部 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69144733/