c# - 使用具有复杂过滤器语法的外部 API

标签 c# architecture domain-driven-design clean-architecture decoupling

我需要对来自外部 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/

相关文章:

c# - 检查空字符串属性的函数?

architecture - 使用领域驱动设计进行建模时,我应该将应用程序特定的东西放在哪里?

c# - 在 DDD 中管理持久性

domain-driven-design - 用于具有多个数据库的复合 .NET 应用程序的 DDD/CQRS

c# - 如何查询 Entity Framework 实体容器以获取派生实体类的对象?

c# - SQLite 加入嵌入式数据库表

architecture - 将顺序内聚转变为功能内聚?

design-patterns - 在域对象方法中封装服务调用

javascript - 是否有像 Breeze 这样的 java 脚本库允许查询 IQueryable 方法而不是属性?

C# - 无法比较数组中的两个元素