制作网站上海,电话销售企业网站怎么做,晋江在线网站建设,建筑企业资质查询系统在之前《在ASP.NET Core中使用Apworks快速开发数据服务》一文的评论部分#xff0c;.NET大神张善友为我提了个建议#xff0c;可以使用Compile As a Service的Roslyn为语法解析提供支持。在此非常感激友哥给我的建议#xff0c;也让我了解了一些Roslyn的知识。使用Roslyn的一…在之前《在ASP.NET Core中使用Apworks快速开发数据服务》一文的评论部分.NET大神张善友为我提了个建议可以使用Compile As a Service的Roslyn为语法解析提供支持。在此非常感激友哥给我的建议也让我了解了一些Roslyn的知识。使用Roslyn的一个很大的好处是框架无需依赖第三方的组件并且Roslyn也是.NET Foundation的一个开源项目为.NET语言提供编译服务社区支持做的也非常出色。然而经过一段时间的思考我还是选择了一个折中的方案在Apworks中使用Irony作为查询语言的语法解析器与此同时为查询语言语法解析提供可扩展的框架级支持。
那么问题来了为什么我需要在Apworks中设计查询语言Irony是什么如何使用Irony实现自己的查询语言语法解析器下面我就一一为大家介绍。
Apworks中的查询语言
很多体验过Apworks数据服务Apworks Data Services案例TaskList的读者肯定有这样的感受为什么每次我新建的任务项目Task Item都是出现在列表中不确定的位置难道新建的任务就不应该放在最前面吗是的你的疑问没有错在之前的TaskList中的确存在这样的问题因为那时候Apworks数据服务在返回任务列表时还不支持查询和排序也就是说它只能默认以Id作为升序进行分页返回所有的数据。当然在最近一版的Apworks数据服务中通过基于Irony的语法解析器已经能够成功地支持查询和排序了。
如果你之前有仔细阅读《在ASP.NET Core中使用Apworks快速开发数据服务》一文并按照文中的演练步骤实现过一个简单的RESTful服务的话那么请你重新在Visual Studio 2017中打开你的解决方案将Apworks相关库更新到最新版本然后不要修改任何代码直接运行你的应用。等应用程序运行后执行一次GET请求URL中你就可以使用query作为查询条件输入了。比如使用curl执行下面的命令 curl -G http://localhost:58928/api/customers --data-urlencode queryname sw \fr\
你将得到下面的结果 可以看到数据服务返回了所有Name字段以“fr”开头的客户信息。当然还支持排序操作。比如执行下面的命令 curl -G http://localhost:58928/api/customers --data-urlencode sortname d
将得到下面的结果 此时返回结果已经按Name字段倒序排列。
在Apworks中查询语言支持以下操作和运算
逻辑运算AND OR NOT关系运算EQ相等NE不等LT小于LE小于等于GT大于GE大于等于字符串运算SW以某字符串开头、EW以某字符串结尾、CT包含某字符串括号优先级日期类型的比对
排序语言支持升序用字母a表示以及降序用字母d表示多个排序条件使用AND关键字连接。例如name a AND email d表示使用name字段做升序排序并以email做降序排序。
以上就给大家大概介绍了一下Apworks数据服务对查询和排序的支持功能。设计这部分功能的需求是显而易见的开发人员无需为一般的查询和排序功能自定义额外的接口。或许你会问为何不使用已有的框架比如OData。不错OData的确可以提供统一的查询界面做系统集成也会相对容易但一方面我还是觉得OData太重Apworks数据服务我希望能够提供更加简单便捷的功能另一方面看上去目前OData还不支持.NET Core应该是不支持我不太确定有知道的朋友也欢迎留言指正。
实现这套查询和排序语法我使用的是一个.NET下开源的语法解析器生成工具集它的名字叫做Irony。
Irony简介
Irony项目最开始是发布在微软的Codeplex代码托管服务上的地址是http://irony.codeplex.com/。在Codeplex上的好评数有51颗星也已经很不错了。可惜的是最近一次更新是在2013年12月看起来已经停止维护了不过之前使用了一下感觉这个项目确实不错不仅提供了开发库而且还有一个图形化的语法解析器的测试工具在写完自己的自定义语言的语法之后还可以通过这个工具进行测试。于是我把它迁移到了Github成为我的一个公共repo地址是https://github.com/daxnet/irony。当然我沿用了原有的MIT许可协议并在首页的README.md中提供了原始地址很可惜Codeplex将在年底关闭并保留了开发者的名字。不仅如此在一番踩坑之后我把它迁移到了.NET Core平台。
在我的Irony Github Repo里提供了一个非常简单的案例就是实现四则混合运算的字符串解析并计算最终结果。当然这个案例也被包含在了这个项目的源代码里。大家可以自己下载查看。
Irony的一个特色就是运用了C#的运算符重载使得语法定义借用了C#的编译功能语法、类型检查等简单直观又不容易出错。比如在如下案例中的语法定义类型中 [Language( xpression Grammar , 1.0 , abc )] public class ExpressionGrammar : Grammar { /// summary /// Initializes a new instance of the see crefExpressionGrammar/ class. /// /summary public ExpressionGrammar() : base ( false ) { var number new NumberLiteral( Number ); number.DefaultIntTypes new TypeCode[] { TypeCode.Int16, TypeCode.Int32, TypeCode.Int64 }; number.DefaultFloatType TypeCode.Single; var identifier new IdentifierTerminal( Identifier ); var comma ToTerm( , ); var BinOp new NonTerminal( BinaryOperator , operator ); var ParExpr new NonTerminal( ParenthesisExpression ); var BinExpr new NonTerminal( BinaryExpression , typeof (BinaryOperationNode)); var Expr new NonTerminal( Expression ); var Term new NonTerminal( Term ); var Program new NonTerminal( Program , typeof (StatementListNode)); Expr.Rule Term | ParExpr | BinExpr; Term.Rule number | identifier; ParExpr.Rule ( Expr ) ; BinExpr.Rule Expr BinOp Expr; BinOp.Rule ToTerm( ) | - | * | / ; RegisterOperators(10, , - ); RegisterOperators(20, * , / ); MarkPunctuation( ( , ) ); RegisterBracePair( ( , ) ); MarkTransient(Expr, Term, BinOp, ParExpr); this .Root Expr; } }
从中可以很容易理解运算符BinOp包含、-、*和/而一个二元运算的表达式BinExpr由两个表达式Expr和一个运算符BinOp组成而二元运算的表达式又是表达式Expr的一种。通过这样的语法定义就可以使用Irony的Parser产生语法树了 var language new LanguageData( new ExpressionGrammar()); var parser new Parser(language); var syntaxTree parser.Parse(input);
怎么样是不是非常方便
在迁移Irony项目的同时我还将Irony的测试工具Irony Grammar Explorer分离出来成为了一个单独的Github Repo。在你定义了上面的ExpressionGrammar类之后编译你的程序集然后就可以使用Irony Grammar Explorer进行测试了。比如使用Irony Grammar Explorer打开Apworks.Querying.Parsers.Irony程序集它将自动扫描程序集中所有的Grammar定义然后让用户对各种Grammar进行测试。值得一提的是在测试界面Irony Grammar Explorer还能根据语法定义自动产生语法高亮 点击右边的语法树中的节点即可定位到输入字符串的相应部分。比较有趣的一点是在Irony Grammar Explorer的Github Repo里还包含了一个语法定义的案例库IronyExplorer.Samples它包含了很多流行编程语言的语法定义。比如下面是C# 3.5语言的语法测试效果 有关Irony Grammar Explorer的其它功能我就不一一介绍了大家可以自己实践一下。总的来说Irony可以帮助大家快速方便地实现语法解析器而且功能也能够满足绝大多数需求针对.NET Core的支持也使得Irony能够直接被应用在跨平台的.NET应用程序中并支持Docker部署。接下来的问题就更有趣了我已经定义了自己的语法并使用Irony Grammar Explorer通过了测试接下来我如何在我的应用程序中运用这个语法换个方式问我拿到了语法树后该怎么办呢
语法树的处理
虽然我们能够将字符串文本解析成一棵语法树能够通过语法树来体现一个字符串中各个部分的含义以及它们之间的关系但是如何能够让计算机来读懂这棵树并执行相应的任务呢这就涉及到语法树的处理问题。参考编译原理词法分析和语法分析已经由Irony完成接下来的语义分析就需要我们自己写代码了。
在Irony Repo的案例代码中我们的目的是能够解析一个四则运算表达式并计算出结果于是我们定义了下面的对象模型 frameborder0 scrollingno styleborder-width: medium; width: 650px; height: 494px;
因此只需要将解析的语法树转换成上面的对象模型也就能够通过Evaluation.Value属性得到计算的最终结果。从代码上看向对象模型的转换是通过递归的方式遍历语法树实现的 private Evaluation PerformEvaluate(ParseTreeNode node) { switch (node.Term.Name) { case BinaryExpression : var leftNode node.ChildNodes[0]; var opNode node.ChildNodes[1]; var rightNode node.ChildNodes[2]; Evaluation left PerformEvaluate(leftNode); Evaluation right PerformEvaluate(rightNode); BinaryOperation op BinaryOperation.Add; switch (opNode.Term.Name) { case : op BinaryOperation.Add; break ; case - : op BinaryOperation.Sub; break ; case * : op BinaryOperation.Mul; break ; case / : op BinaryOperation.Div; break ; } return new BinaryEvaluation(left, right, op); case Number : var value Convert.ToSingle(node.Token.Text); return new ConstantEvaluation(value); } throw new InvalidOperationException($ Unrecognizable term {node.Term.Name}. ); }
以上完整代码请参考Evaluator的实现。整个案例及使用方式可以点击https://github.com/daxnet/irony#example查看。可以看到使用Irony来实现一个四则混合运算的计算器还是非常方便的。
在Apworks中我们需要的是能够将一个表达查询语义的语法树转换成Lambda表达式以便于后台数据库引擎能够直接执行Lambda表达式完成查询。通过数据库引擎执行Lambda表达式的优势是非常明显的比如Entity Framework Core可以通过Lambda表达式生成高效的SQL语句并在数据库服务器上执行性能方面也能兼顾得非常好。
类似的我们使用.NET Expression的对象模型通过遍历查询语句的语法树来生成表达式模型最后转换成Lambda表达式即可。具体过程就不再赘述了请参考Apworks的源代码。现在我们来看看实际效果。
假设我们的测试数据如下 Customers.Add( new Customer { Id 1, Email jimexample.com , Name jim , DateRegistered DateTime.Now.AddDays(-1) }); Customers.Add( new Customer { Id 2, Email tomexample.com , Name tom , DateRegistered DateTime.Now.AddDays(-2) }); Customers.Add( new Customer { Id 3, Email alexexample.com , Name alex , DateRegistered DateTime.Now.AddDays(-3) }); Customers.Add( new Customer { Id 4, Email carolexample.com , Name carol , DateRegistered DateTime.Now.AddDays(-4) }); Customers.Add( new Customer { Id 5, Email davidexample.com , Name david , DateRegistered DateTime.Now.AddDays(-5) }); Customers.Add( new Customer { Id 6, Email frankexample.com , Name frank , DateRegistered DateTime.Now.AddDays(-6) }); Customers.Add( new Customer { Id 7, Email peterexample.com , Name peter , DateRegistered DateTime.Now.AddDays(-7) }); Customers.Add( new Customer { Id 8, Email paulexample.com , Name paul , DateRegistered DateTime.Now.AddDays(1) }); Customers.Add( new Customer { Id 9, Email winterexample.com , Name winter , DateRegistered DateTime.Now.AddDays(2) }); Customers.Add( new Customer { Id 10, Email julieexample.com , Name julie , DateRegistered DateTime.Now.AddDays(3) }); Customers.Add( new Customer { Id 11, Email jimexample.com , Name jim , DateRegistered DateTime.Now.AddDays(4) }); Customers.Add( new Customer { Id 12, Email brianexample.com , Name brian , DateRegistered DateTime.Now.AddDays(5) }); Customers.Add( new Customer { Id 13, Email davidexample.com , Name david , DateRegistered DateTime.Now.AddDays(6) }); Customers.Add( new Customer { Id 14, Email danielexample.com , Name daniel , DateRegistered DateTime.Now.AddDays(7) }); Customers.Add( new Customer { Id 15, Email jillexample.com , Name jill , DateRegistered DateTime.Now.AddDays(8) });
下面调试单元测试并查看所产生的Lambda表达式可以看到Lambda表达式正确产生测试顺利通过 总结
本文介绍了Apworks中自定义查询语句在Apworks数据服务中的应用并介绍了查询语句和排序语句的实现方式与此同时对Irony Grammar Parser进行了介绍。Apworks中查询语句的实现还是相对简单的目前不支持内嵌对象的属性查询比如Customer.Address.Country EQ “China” 这样的查询是不支持的。为了保证实现过程相对简单快速今后也不打算支持。如果需要用到这种内嵌对象属性的查询请扩展DataServiceController以实现自己的特定API来完成。
接下来我会介绍Entity Framework Core在Apworks数据服务中的使用虽然已经预告了好几次了-_-!!。
原文地址http://www.cnblogs.com/daxnet/p/6953418.html .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注