ExpressionFactory`.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Text.RegularExpressions;
  8. using Bowin.Common.Linq;
  9. using System.Collections;
  10. namespace Bowin.Common.Linq.Entity
  11. {
  12. public static class ExpressionFactory
  13. {
  14. public static Expression<Func<TSource, bool>> DynamicExpression<TSource>(this Expression<Func<TSource, bool>> exp,
  15. string propertyName, string comparer, object value)
  16. {
  17. ExpressionFactory<TSource> factory = new ExpressionFactory<TSource>();
  18. Comparer comparerEnum;
  19. if (!factory.comparerDict.TryGetValue(comparer, out comparerEnum))
  20. {
  21. throw new Exception("输入的运算符不受支持。");
  22. }
  23. var propertyList = propertyName.Split('.').ToList();
  24. PropertyInfo propertyInfo = typeof(TSource).GetProperty(propertyList[0]);
  25. ParameterExpression paramter = Expression.Parameter(typeof(TSource), "x");
  26. MemberExpression property = Expression.Property(paramter, propertyList[0]);
  27. Expression result = factory.RecursiveExpression(property, propertyList.Skip(1).ToList(), comparerEnum, value);
  28. return Expression.Lambda<Func<TSource, bool>>(result, paramter);
  29. }
  30. }
  31. public class ExpressionFactory<TSource>
  32. {
  33. private static readonly Dictionary<string, Expression> _cache = new Dictionary<string, Expression>();
  34. private IQueryable<TSource> _queryable;
  35. public Dictionary<string, Comparer> comparerDict = new Dictionary<string, Comparer>()
  36. {
  37. { "=", Comparer.Equal },
  38. { ">", Comparer.GreaterThan },
  39. { "<", Comparer.LessThan },
  40. { ">=", Comparer.GreaterThanOrEqual },
  41. { "<=", Comparer.LessThanOrEqual },
  42. { "<>", Comparer.NotEqual },
  43. { "!=", Comparer.NotEqualEx1 },
  44. { "左", Comparer.Left },
  45. { "中", Comparer.Contains },
  46. { "右", Comparer.Right }
  47. };
  48. public ExpressionFactory()
  49. {
  50. }
  51. public ExpressionFactory(IQueryable<TSource> queryable)
  52. {
  53. _queryable = queryable;
  54. }
  55. public Expression<Func<TSource, bool>> DynamicInExpression<TListType>(string propertyName, IList<TListType> valueList)
  56. {
  57. PropertyInfo propertyInfo = typeof(TSource).GetProperty(propertyName);
  58. ParameterExpression paramter = Expression.Parameter(typeof(TSource), "x");
  59. MemberExpression property = Expression.Property(paramter, propertyName);
  60. var method = valueList.GetType().GetMethod("Contains");
  61. Expression result;
  62. result = Expression.Call(Expression.Constant(valueList), method, property);
  63. return Expression.Lambda<Func<TSource, bool>>(result, paramter);
  64. }
  65. private Expression ParseValue(object value, Type propertyType)
  66. {
  67. if (value.GetType().Name.StartsWith("String") && !propertyType.Name.StartsWith("String"))
  68. {
  69. return Expression.Convert(Expression.Constant(value.ToString().ParseToType(propertyType)), propertyType);
  70. }
  71. else
  72. {
  73. try
  74. {
  75. return Expression.Convert(Expression.Constant(value), propertyType);
  76. }
  77. catch (Exception ex)
  78. {
  79. return Expression.Convert(Expression.Constant(null), propertyType);
  80. }
  81. }
  82. }
  83. public Expression RecursiveExpression(Expression parent, List<string> propertyList, Comparer comparerEnum, object value)
  84. {
  85. Type propertyType;
  86. if (parent is MemberExpression)
  87. {
  88. propertyType = ((PropertyInfo)(((MemberExpression)parent).Member)).PropertyType;
  89. }
  90. else
  91. {
  92. propertyType = ((ParameterExpression)parent).Type;
  93. }
  94. if (typeof(IEnumerable).IsAssignableFrom(propertyType) && !propertyType.Name.StartsWith("String"))
  95. {
  96. var anyMethods = typeof(Enumerable).GetMethods().Where(x => x.Name == "Any");
  97. var anyMethod = anyMethods.FirstOrDefault(x => x.GetParameters().Length == 2);
  98. var elementType = propertyType.GetGenericArguments().FirstOrDefault();
  99. anyMethod = anyMethod.MakeGenericMethod(elementType);
  100. ParameterExpression paramter = Expression.Parameter(elementType);
  101. if (propertyList.Count > 0)
  102. {
  103. var anyExpression = RecursiveExpression(
  104. Expression.Property(paramter, elementType.GetProperty(propertyList[0])),
  105. propertyList.Skip(1).ToList(),
  106. comparerEnum,
  107. value);
  108. return Expression.Call(anyMethod, parent, Expression.Lambda(anyExpression, paramter));
  109. }
  110. else
  111. {
  112. return Expression.Call(parent, anyMethod, Expression.Equal(paramter, this.ParseValue(value, elementType)));
  113. }
  114. }
  115. else if (!(propertyType.IsValueType || propertyType.Name.StartsWith("String")))
  116. {
  117. return RecursiveExpression(parent, propertyList.Skip(1).ToList(), comparerEnum, value);
  118. }
  119. else
  120. {
  121. MemberExpression property = (MemberExpression)parent;
  122. PropertyInfo propertyInfo = (PropertyInfo)(property.Member);
  123. Expression valueExp = this.ParseValue(value, propertyInfo.PropertyType);
  124. Expression result;
  125. switch (comparerEnum)
  126. {
  127. case Comparer.Equal:
  128. if (propertyInfo.PropertyType.FullName.Contains("System.DateTime"))
  129. {
  130. DateTime tmp;
  131. if (value == null)
  132. {
  133. result = Expression.Equal(property, valueExp);
  134. }
  135. else
  136. {
  137. tmp = DateTime.Parse(value.ToString());
  138. if (tmp.Hour == 0 && tmp.Minute == 0 && tmp.Second == 0 && tmp.Millisecond == 0) //判断是否有值并且只剩下日期部分
  139. {
  140. result = Expression.And(Expression.GreaterThanOrEqual(property, valueExp), Expression.LessThan(property, this.ParseValue(tmp.AddDays(1), propertyInfo.PropertyType)));
  141. }
  142. else
  143. {
  144. result = Expression.Equal(property, valueExp);
  145. }
  146. }
  147. }
  148. else
  149. {
  150. result = Expression.Equal(property, valueExp);
  151. }
  152. break;
  153. case Comparer.GreaterThan:
  154. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  155. {
  156. Expression propertyExpression = Expression.Call(
  157. property, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), Expression.Constant(value));
  158. result = Expression.GreaterThan(propertyExpression, Expression.Constant(0));
  159. }
  160. else
  161. {
  162. result = Expression.GreaterThan(property, valueExp);
  163. }
  164. break;
  165. case Comparer.LessThan:
  166. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  167. {
  168. Expression propertyExpression = Expression.Call(
  169. property, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), Expression.Constant(value));
  170. result = Expression.LessThan(propertyExpression, Expression.Constant(0));
  171. }
  172. else
  173. {
  174. result = Expression.LessThan(property, valueExp);
  175. }
  176. break;
  177. case Comparer.GreaterThanOrEqual:
  178. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  179. {
  180. Expression propertyExpression = Expression.Call(
  181. property, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), Expression.Constant(value));
  182. result = Expression.GreaterThanOrEqual(propertyExpression, Expression.Constant(0));
  183. }
  184. else
  185. {
  186. result = Expression.GreaterThanOrEqual(property, valueExp);
  187. }
  188. break;
  189. case Comparer.LessThanOrEqual:
  190. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  191. {
  192. Expression propertyExpression = Expression.Call(
  193. property, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), Expression.Constant(value));
  194. result = Expression.LessThanOrEqual(propertyExpression, Expression.Constant(0));
  195. }
  196. else
  197. {
  198. result = Expression.LessThanOrEqual(property, valueExp);
  199. }
  200. break;
  201. case Comparer.NotEqual:
  202. case Comparer.NotEqualEx1:
  203. result = Expression.NotEqual(property, valueExp);
  204. break;
  205. case Comparer.Left:
  206. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  207. {
  208. if (value != null)
  209. {
  210. result = Expression.Call(
  211. property, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), Expression.Constant(value));
  212. }
  213. else
  214. {
  215. result = Expression.Constant(true);
  216. }
  217. }
  218. else
  219. {
  220. throw new Exception("只有字符串属性才可以使用‘左’、‘中’、‘右’操作符。");
  221. }
  222. break;
  223. case Comparer.Contains:
  224. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  225. {
  226. if (value != null)
  227. {
  228. result = Expression.Call(
  229. property, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(value));
  230. }
  231. else
  232. {
  233. result = Expression.Constant(true);
  234. }
  235. }
  236. else
  237. {
  238. throw new Exception("只有字符串属性才可以使用‘左’、‘中’、‘右’操作符。");
  239. }
  240. break;
  241. case Comparer.Right:
  242. if (propertyInfo.PropertyType.Name.StartsWith("String"))
  243. {
  244. if (value != null)
  245. {
  246. result = Expression.Call(
  247. property, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), Expression.Constant(value));
  248. }
  249. else
  250. {
  251. result = Expression.Constant(true);
  252. }
  253. }
  254. else
  255. {
  256. throw new Exception("只有字符串属性才可以使用‘左’、‘中’、‘右’操作符。");
  257. }
  258. break;
  259. default:
  260. throw new Exception("输入的运算符不受支持。");
  261. }
  262. return result;
  263. }
  264. }
  265. public Expression<Func<TSource, bool>> DynamicExpression(string propertyName, string comparer, object value)
  266. {
  267. Comparer comparerEnum;
  268. if (!comparerDict.TryGetValue(comparer, out comparerEnum))
  269. {
  270. throw new Exception("输入的运算符不受支持。");
  271. }
  272. var propertyList = propertyName.Split('.').ToList();
  273. PropertyInfo propertyInfo = typeof(TSource).GetProperty(propertyList[0]);
  274. ParameterExpression paramter = Expression.Parameter(typeof(TSource), "x");
  275. MemberExpression property = Expression.Property(paramter, propertyList[0]);
  276. Expression result = RecursiveExpression(property, propertyList.Skip(1).ToList(), comparerEnum, value);
  277. return Expression.Lambda<Func<TSource, bool>>(result, paramter);
  278. }
  279. /// <summary>
  280. /// 影射的指定类型。
  281. /// </summary>
  282. /// <typeparam name="TResult"></typeparam>
  283. /// <returns></returns>
  284. public IQueryable<TResult> To<TResult>()
  285. {
  286. var selectExpression = GetSelectExpression<TResult>();
  287. return _queryable.Select(selectExpression);
  288. }
  289. private static Expression<Func<TSource, TResult>> GetSelectExpression<TResult>()
  290. {
  291. var key = GetKey<TResult>();
  292. Expression<Func<TSource, TResult>> selectExpression;
  293. if (_cache.ContainsKey(key))
  294. {
  295. selectExpression = _cache[key] as Expression<Func<TSource, TResult>>;
  296. }
  297. else
  298. {
  299. selectExpression = BuildExpression<TResult>();
  300. _cache[key] = selectExpression;
  301. }
  302. return selectExpression;
  303. }
  304. private static string GetKey<TResult>()
  305. {
  306. return string.Concat(typeof(TSource).FullName, typeof(TResult).FullName);
  307. }
  308. private static Expression<Func<TSource, TResult>> BuildExpression<TResult>()
  309. {
  310. var sourceProperties = typeof(TSource).GetProperties();
  311. var resultProperties = typeof(TResult).GetProperties().Where(w => w.CanWrite);
  312. var parameterExpression = Expression.Parameter(typeof(TSource), "source");
  313. var bindings = resultProperties.Select(s => BuildBinding(parameterExpression, s, sourceProperties)).Where(w => w != null);
  314. var expression = Expression.Lambda<Func<TSource, TResult>>(Expression.MemberInit(Expression.New(typeof(TResult)), bindings), parameterExpression);
  315. return expression;
  316. }
  317. private static MemberAssignment BuildBinding(Expression parameterExpression, MemberInfo resultProperty, IEnumerable<PropertyInfo> sourceProperties)
  318. {
  319. var sourceProperty = sourceProperties.FirstOrDefault(w => w.Name == resultProperty.Name);
  320. var propertyNames1 = SplitCamelCase(resultProperty.Name);
  321. if (sourceProperty != null)
  322. {
  323. return Expression.Bind(resultProperty, Expression.Property(parameterExpression, sourceProperty));
  324. }
  325. var propertyNames = SplitCamelCase(resultProperty.Name);
  326. if (propertyNames.Length == 2)
  327. {
  328. sourceProperty = sourceProperties.FirstOrDefault(src => src.Name == propertyNames[0]);
  329. if (sourceProperty != null)
  330. {
  331. var sourceChildProperty = sourceProperty.PropertyType.GetProperties().FirstOrDefault(src => src.Name == propertyNames[1]);
  332. if (sourceChildProperty != null)
  333. {
  334. return Expression.Bind(resultProperty, Expression.Property(Expression.Property(parameterExpression, sourceProperty), sourceChildProperty));
  335. }
  336. }
  337. }
  338. return null;
  339. }
  340. private static string[] SplitCamelCase(string input)
  341. {
  342. var ss = Regex.Replace(input, "([A-Z])", " $1");
  343. return Regex.Replace(input, "([A-Z])", " $1").Trim().Split(' ');
  344. }
  345. }
  346. public enum Comparer
  347. {
  348. /// <summary>
  349. /// <!-- = -->
  350. /// </summary>
  351. Equal = 0,
  352. /// <summary>
  353. /// <!-- > -->
  354. /// </summary>
  355. GreaterThan = 1,
  356. /// <summary>
  357. /// <!-- < -->
  358. /// </summary>
  359. LessThan = 2,
  360. /// <summary>
  361. /// <!-- >= -->
  362. /// </summary>
  363. GreaterThanOrEqual = 3,
  364. /// <summary>
  365. /// <!-- <= -->
  366. /// </summary>
  367. LessThanOrEqual = 4,
  368. /// <summary>
  369. /// <!-- <> -->
  370. /// </summary>
  371. NotEqual = 5,
  372. /// <summary>
  373. /// <!-- != -->
  374. /// </summary>
  375. NotEqualEx1 = 6,
  376. /// <summary>
  377. /// <!-- 左 -->
  378. /// </summary>
  379. Left = 7,
  380. /// <summary>
  381. /// <!-- 中 -->
  382. /// </summary>
  383. Contains = 8,
  384. /// <summary>
  385. /// <!-- 右 -->
  386. /// </summary>
  387. Right = 9
  388. }
  389. }