QueryTranslator.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Reflection;
  8. using System.Diagnostics;
  9. namespace XYY.Core.Standard.Data.Infrastructure
  10. {
  11. public class QueryTranslator
  12. {
  13. internal class SplitExpression
  14. {
  15. public Expression Expression { get; set; }
  16. public string InnerLink { get; set; }
  17. public string OutLink { get; set; }
  18. public string Flag { get; set; }
  19. }
  20. public string SqlWhere;
  21. /// <summary>
  22. /// 根据条件生成对应的sql查询操作符
  23. /// </summary>
  24. /// <param name="expressiontype"></param>
  25. /// <returns></returns>
  26. private string GetOperator(ExpressionType expressiontype)
  27. {
  28. switch (expressiontype)
  29. {
  30. case ExpressionType.And:
  31. return "and";
  32. case ExpressionType.AndAlso:
  33. return "and";
  34. case ExpressionType.Or:
  35. return "or";
  36. case ExpressionType.OrElse:
  37. return "or";
  38. case ExpressionType.Equal:
  39. return "=";
  40. case ExpressionType.NotEqual:
  41. return "<>";
  42. case ExpressionType.LessThan:
  43. return "<";
  44. case ExpressionType.LessThanOrEqual:
  45. return "<=";
  46. case ExpressionType.GreaterThan:
  47. return ">";
  48. case ExpressionType.GreaterThanOrEqual:
  49. return ">=";
  50. default:
  51. throw new Exception(string.Format("不支持{0}此种运算符查找!" + expressiontype));
  52. }
  53. }
  54. public string Translate(Expression expression)
  55. {
  56. List<SplitExpression> splitExpression = new List<SplitExpression>();
  57. Resolve(expression, expression.NodeType, ref splitExpression);
  58. this.SqlWhere = AnalyzeExpression(splitExpression);
  59. return SqlWhere;
  60. }
  61. private void Resolve(Expression expression, ExpressionType link, ref List<SplitExpression> list)
  62. {
  63. if (expression is LambdaExpression)
  64. {
  65. LambdaExpression lambda = expression as LambdaExpression;
  66. Resolve(lambda.Body, lambda.NodeType, ref list);
  67. }
  68. string guid = Guid.NewGuid().ToString();
  69. if (expression.NodeType == ExpressionType.AndAlso || expression.NodeType == ExpressionType.OrElse)
  70. {
  71. if (expression is BinaryExpression)
  72. {
  73. var binary = (expression as BinaryExpression);
  74. if (binary.Left.NodeType == ExpressionType.AndAlso || binary.Left.NodeType == ExpressionType.OrElse)
  75. {
  76. Resolve(binary.Left, expression.NodeType, ref list);
  77. }
  78. else
  79. {
  80. list.Add(new SplitExpression()
  81. {
  82. Expression = binary.Left,
  83. InnerLink = binary.NodeType.ToString(),
  84. Flag = guid,
  85. OutLink = link.ToString()
  86. });
  87. }
  88. if (binary.Right.NodeType == ExpressionType.AndAlso || binary.Right.NodeType == ExpressionType.OrElse)
  89. {
  90. Resolve(binary.Right, expression.NodeType, ref list);
  91. }
  92. else
  93. {
  94. list.Add(new SplitExpression()
  95. {
  96. Expression = binary.Right,
  97. InnerLink = binary.NodeType.ToString(),
  98. Flag = guid,
  99. OutLink = link.ToString()
  100. });
  101. }
  102. }
  103. }
  104. else
  105. {
  106. if (expression is BinaryExpression)
  107. {
  108. var binary = (expression as BinaryExpression);
  109. if (binary != null)
  110. {
  111. list.Add(new SplitExpression()
  112. {
  113. Expression = binary,
  114. Flag = guid
  115. });
  116. }
  117. }
  118. else if (expression is MemberExpression)
  119. {
  120. var member = (expression as MemberExpression);
  121. if (member != null)
  122. {
  123. list.Add(new SplitExpression()
  124. {
  125. Expression = member,
  126. Flag = guid
  127. });
  128. }
  129. }
  130. else if (expression is MethodCallExpression)
  131. {
  132. var callMethod = (expression as MethodCallExpression);
  133. list.Add(new SplitExpression()
  134. {
  135. Expression = callMethod,
  136. Flag = guid
  137. });
  138. }
  139. }
  140. }
  141. private string AnalyzeExpression(List<SplitExpression> expression)
  142. {
  143. string where = string.Empty;
  144. var group = expression.GroupBy(x => x.Flag).Select(x => x.Key);
  145. foreach (var flag in group)
  146. {
  147. var splitExpression = expression.Where(x => x.Flag == flag);
  148. where += " (";
  149. string innerWhere = "";
  150. string link = "";
  151. foreach (var item in splitExpression)
  152. {
  153. item.OutLink = item.OutLink ?? "";
  154. item.InnerLink = item.InnerLink ?? "";
  155. link = item.OutLink;
  156. #if DEBUG
  157. Trace.WriteLine("OutLink:" + link);
  158. #endif
  159. var express = item.Expression;
  160. if (express is UnaryExpression)
  161. {
  162. UnaryExpression unary = express as UnaryExpression;
  163. if (unary.Operand is MethodCallExpression)//解析!x=>x.Name.Contains("xxx")或!array.Contains(x.Name)这类
  164. return ResolveLinqToObject(unary.Operand, false);
  165. if (unary.Operand is MemberExpression && unary.NodeType == ExpressionType.Not)//解析x=>!x.isDeletion这样的
  166. {
  167. ConstantExpression constant = Expression.Constant(false);
  168. innerWhere += ResolveFunc(unary.Operand, constant, ExpressionType.Equal) + (item.InnerLink == "AndAlso" ? " AND " : " OR ");
  169. }
  170. }
  171. else if (express is MethodCallExpression)
  172. {
  173. MethodCallExpression methodcall = express as MethodCallExpression;
  174. innerWhere += ResolveLinqToObject(methodcall, true) + (item.InnerLink == "AndAlso" ? " AND " : " OR ");
  175. #if DEBUG
  176. Trace.WriteLine("where:" + innerWhere + "; InnerLink:" + item.InnerLink);
  177. #endif
  178. }
  179. else if (express is BinaryExpression)
  180. {
  181. BinaryExpression binary = express as BinaryExpression;
  182. dynamic value = null;
  183. if ((binary.Right is MemberExpression && binary.Right.NodeType == ExpressionType.MemberAccess)
  184. ||
  185. (binary.Right is MethodCallExpression && binary.Right.NodeType == ExpressionType.Call)
  186. ||
  187. (binary.Right is UnaryExpression && binary.Right.NodeType == ExpressionType.Convert)
  188. )
  189. {
  190. LambdaExpression lambda = Expression.Lambda(binary.Right);
  191. Delegate fn = lambda.Compile();
  192. value = Expression.Constant(fn.DynamicInvoke(null), binary.Right.Type);
  193. }
  194. else
  195. {
  196. value = binary.Right;
  197. }
  198. string property = GetPropertyName(binary);
  199. string opr = GetOperator(binary.NodeType);
  200. string condition;
  201. if (value.Value != null)
  202. {
  203. string conditionString = "{0} {1} N'{2}'";
  204. if (binary.Right.Type.Equals(typeof(DateTime)) || binary.Right.Type.Equals(typeof(Nullable<DateTime>)))
  205. {
  206. property = "Convert(varchar(10),{0},23)".Formater(property);
  207. value = string.Format("Convert(varchar,'{0}',23)", value.Value.ToString("yyyy-MM-dd hh:mm:ss"));
  208. conditionString = "{0} {1} {2}";
  209. }
  210. else
  211. {
  212. value = value.Value.ToString();
  213. }
  214. condition = string.Format(conditionString, property, opr, value) + (item.InnerLink == "AndAlso" ? " AND " : " OR ");
  215. }
  216. else
  217. {
  218. condition = string.Format("{0} is NULL{1}", property, item.InnerLink == "AndAlso" ? " AND " : " OR ");
  219. }
  220. innerWhere += condition;
  221. }
  222. else if (express is MemberExpression)
  223. {
  224. MemberExpression member = express as MemberExpression;
  225. ConstantExpression constant = Expression.Constant(true);
  226. innerWhere += ResolveFunc(member, constant, ExpressionType.Equal) + (item.InnerLink == "AndAlso" ? " AND " : " OR ");
  227. }
  228. }
  229. innerWhere = innerWhere.Substring(0, innerWhere.Length - 4);
  230. where += innerWhere;
  231. where += string.Format(") {0}", link == "AndAlso" ? " AND " : " OR ");
  232. }
  233. return where.Substring(0, where.Length - 4);
  234. }
  235. private string ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)
  236. {
  237. var Name = (left as MemberExpression).Member.Name;
  238. var Value = (right as ConstantExpression).Value;
  239. var Operator = GetOperator(expressiontype);
  240. //string CompName = SetArgument(Name, Value.ToString());
  241. string Result = string.Format("({0} {1} N'{2}')", Name, Operator, Value);
  242. return Result;
  243. }
  244. private string ResolveLinqToObject(Expression expression, object value, ExpressionType? expressiontype = null)
  245. {
  246. var MethodCall = expression as MethodCallExpression;
  247. var MethodName = MethodCall.Method.Name;
  248. switch (MethodName)//这里其实还可以改成反射调用,不用写switch
  249. {
  250. case "Contains":
  251. if (MethodCall.Object != null && !MethodCall.Object.Type.IsGenericType)
  252. return Like(MethodCall);
  253. return In(MethodCall, value);
  254. case "Count":
  255. return Len(MethodCall, value, expressiontype.Value);
  256. case "LongCount":
  257. return Len(MethodCall, value, expressiontype.Value);
  258. case "ToLower":
  259. return ToLower(MethodCall, value, expressiontype.Value);
  260. case "In":
  261. return InCall(MethodCall, value);
  262. case "StartsWith":
  263. if (MethodCall.Object != null && !MethodCall.Object.Type.IsGenericType)
  264. return Like(MethodCall, true);
  265. return In(MethodCall, value);
  266. default:
  267. throw new Exception(string.Format("不支持{0}方法的查找!", MethodName));
  268. }
  269. }
  270. private string InCall(MethodCallExpression expression, object isTrue)
  271. {
  272. string field = "";
  273. if ((expression.Arguments[0] as MemberExpression) != null)
  274. {
  275. field = (expression.Arguments[0] as MemberExpression).Member.Name;
  276. }
  277. else
  278. {
  279. field = (((expression.Arguments[0] as MethodCallExpression).Arguments[0] as UnaryExpression).Operand as MemberExpression).Member.Name;
  280. }
  281. var member = (expression.Arguments[1] as MemberExpression);
  282. var values = ((ConstantExpression)member.Expression).Value;
  283. var list = values.GetType().GetField(member.Member.Name).GetValue(values) as IEnumerable<int>;
  284. //var values = ((expression.Arguments[1] as MemberExpression).Expression as ConstantExpression).Value;
  285. //var list = values.GetType().GetFields()[0].GetValue(values) as IEnumerable<int>;
  286. List<string> inElement = new List<string>();
  287. foreach (var item in list)
  288. {
  289. inElement.Add(item.ToString());
  290. }
  291. string _operator = Convert.ToBoolean(isTrue) ? "IN" : " NOT IN";
  292. return "{0} {1} ({2})".Formater(field, _operator, string.Join(",", inElement));
  293. }
  294. private string In(MethodCallExpression expression, object isTrue)
  295. {
  296. var Argument1 = (expression.Arguments[0] as MemberExpression).Expression as ConstantExpression;
  297. var Argument2 = expression.Arguments[1] as MemberExpression;
  298. var Field_Array = Argument1.Value.GetType().GetFields().First();
  299. var fieldVal = Field_Array.GetValue(Argument1.Value);
  300. object[] Array = fieldVal as object[];
  301. if (Array == null)
  302. {
  303. if (fieldVal is IEnumerable<string>)
  304. {
  305. Array = (fieldVal as IEnumerable<string>).Select(i => $"'{i}'").ToArray();
  306. }
  307. }
  308. List<string> SetInPara = new List<string>();
  309. for (int i = 0; i < Array.Length; i++)
  310. {
  311. //string Name_para = "InParameter" + i;
  312. string Value = Array[i].ToString();
  313. //string Key = SetArgument(Name_para, Value);
  314. SetInPara.Add(Value);
  315. }
  316. string Name = Argument2.Member.Name;
  317. string Operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
  318. string CompName = string.Join(",", SetInPara);
  319. string Result = string.Format("{0} {1} ({2})", Name, Operator, CompName);
  320. return Result;
  321. }
  322. private string Like(MethodCallExpression expression,bool isStartWith=false)
  323. {
  324. object Temp_Vale = null;
  325. if ((expression.Arguments[0] is ConstantExpression))
  326. {
  327. Temp_Vale = (expression.Arguments[0] as ConstantExpression).Value;
  328. }
  329. else if (expression.Arguments[0] is MemberExpression)
  330. {
  331. var arg = expression.Arguments[0] as MemberExpression;
  332. var argVal = Expression.Lambda(arg).Compile().DynamicInvoke();
  333. Temp_Vale = argVal;
  334. }
  335. string Value = isStartWith ? string.Format("'{0}%'", Temp_Vale) : string.Format("'%{0}%'", Temp_Vale);
  336. string Name = (expression.Object as MemberExpression).Member.Name;
  337. // string CompName = SetArgument(Name, Value);
  338. string Result = string.Format("{0} LIKE {1}", Name, Value);
  339. return Result;
  340. }
  341. private string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)
  342. {
  343. object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
  344. string Operator = GetOperator(expressiontype);
  345. //string CompName = SetArgument(Name.ToString(), value.ToString());
  346. string Result = string.Format("len({0}){1}{2}", Name, Operator, value);
  347. return Result;
  348. }
  349. private string ToLower(MethodCallExpression expression, object value, ExpressionType expressiontype)
  350. {
  351. return (expression.Arguments[0] as MemberExpression).Member.Name.ToLower();
  352. }
  353. private static string GetPropertyName(BinaryExpression body)
  354. {
  355. string leftStr = body.Left.ToString();
  356. if(body.Left.NodeType==ExpressionType.Convert)
  357. {
  358. var t = (body.Left as UnaryExpression)?.Operand.ToString();
  359. leftStr = t;
  360. }
  361. string propertyName = leftStr.Split(new char[] { '.' })[1];
  362. if (body.Left.NodeType == ExpressionType.Convert)
  363. {
  364. // hack to remove the trailing ) when convering.
  365. propertyName = propertyName.Replace(")", string.Empty);
  366. }
  367. return propertyName;
  368. }
  369. }
  370. }