编写正则表达式模式以查找没有Select方法的IQueryable链式方法

时间:2017-01-30 14:52:53

标签: c# regex entity-framework

在Web应用程序中,我是开发人员团队的一员,我们将Entity Framework用作ORM。当我们执行SQL查询时,我们通常链接实体框架提供的IQueryable方法,从方法Items()开始,这是DbSets的项目特定模型投影。我一直在尝试编写一个正则表达式模式,该模式可以找到不使用Select方法的查询。

这是我们的代码通常的样子

Cars.Items()
  .Where(x => x.Year == "1988")
  .Select(x => new { x.Registration })
  .ToList();

Cars.Items()
  .Where(x => x.Id == 1923984)
  .Select(x => new { x.Registration })
  .FirstOrDefault;

这是我想要查询的那种查询

Cars.Items()
  .Where(x => x.Id == 1923984)
  .FirstOrDefault;

我尝试使用否定前瞻来排除具有Select()方法的查询,但它们已包含在内,而且我很难想到另一种方法。

\.Items\(.*\)(\s*\..*\(.*\))*(?!\.Select\(.+\))(\s*\..*\(.*\))\;

我打破逻辑

  • \.Items\(.*\)所有查询均以此方法开头
  • (\s*\..*\(.*\))*任意数量的链式IQueryable方法
  • (?!\.Select\(.+\))应排除Select()方法
  • (\s*\..*\(.*\))\这可以是First()FirstOrDefault()Single()或类似的
  • ;结束查询

2 个答案:

答案 0 :(得分:3)

由于您已经捕获了此部分的所有内容,因此前方的负面展望是不起作用的原因:

\.Items\(.*\)(\s*\..*\(.*\))*

你需要首先向前看负面,然后包括一切。试试这个正则表达式:

\.Items\(.*?\)(\W.*(?!\.Select\(.+\)))?(\W.*)?\;

在此处试试:https://regex101.com/r/vu9mQ0/2

答案 1 :(得分:3)

我也试过为此创建一个正则表达式。您可以在此处进行测试:https://regex101.com/r/qRU2sP/5

正则表达式:Cars(?>\.(?!select)\w+(?:\([^)]*\))?\s*)+\;

与答案的答案之间的微小区别在于,这一点反映了整个陈述。

编辑:那么它是如何运作的?

我尝试捕捉重复部分(.Items().Select()),以便更容易维护/遵循模式。

Cars
我已经把Cars放在了开头,因为正如这种方式,正则表达式引擎知道下一组字符是否有趣。这减少了所需的步骤量。也可以由\w+替换,但随后会有更多步骤,因为整个文本中的更多字符与之匹配。

(?>
我在这里开始一个原子团。这意味着正则表达式引擎在遵循正则表达式模式时不会回溯。这大大提高了性能,因为如果模式的一部分失败,它将不会尝试其他可能性。 More detail here.

\.
字面上匹配.字符。

(?!select)
否定前瞻 - 仅在select时匹配。我在regex101.com上使用了不区分大小写的修饰符,但C#也支持这一点。否则只需将其更改为[Ss]elect

\w+
匹配任何单词字符a-zA-Z0-9_至少一次。这与文字的whereselect部分相符。

(?:
我在这里开始一个新的非捕获组。我这样做是因为模式的下一部分是可选的(方法/ lambdas的()部分)。

\(
这只是字面匹配(字符。

[^)]*
这匹配所有内容(包括无。请参阅*)UNTIL遇到)字符。

\)
字面匹配)字符。

)?
我关闭了可选项(请参阅?)组。请记住,这是每个方法/ lambda的()的一部分,但它也可能是一个属性,因此()部分必须是可选的。

\s*
匹配任何空白字符(如空格,制表符和换行符)尽可能多次。

)+
在这里,我们结束与文本的.Where(x => x)部分匹配的组(至少一次。请参阅+)。

\;
字面匹配;字符。

就是这样!因此,性能上最大的欺骗是Cars部分和(?>原子组。