/src/NHibernate/Impl/ExpressionProcessor.cs

https://github.com/whut/nhibernate-core · C# · 835 lines · 602 code · 133 blank · 100 comment · 119 complexity · d4574e8a0eb8704365c14df3cd2cb37f MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using NHibernate.Criterion;
  7. using NHibernate.Util;
  8. using Expression = System.Linq.Expressions.Expression;
  9. namespace NHibernate.Impl
  10. {
  11. /// <summary>
  12. /// Subquery type enumeration
  13. /// </summary>
  14. public enum LambdaSubqueryType
  15. {
  16. /// <summary>exact</summary>
  17. Exact = 1,
  18. /// <summary>all</summary>
  19. All = 2,
  20. /// <summary>some</summary>
  21. Some = 3,
  22. }
  23. /// <summary>
  24. /// Converts lambda expressions to NHibernate criterion/order
  25. /// </summary>
  26. public static class ExpressionProcessor
  27. {
  28. public class ProjectionInfo
  29. {
  30. private string _property;
  31. private IProjection _projection;
  32. protected ProjectionInfo() { }
  33. public static ProjectionInfo ForProperty(string property) { return new ProjectionInfo() { _property = property }; }
  34. public static ProjectionInfo ForProjection(IProjection projection) { return new ProjectionInfo() { _projection = projection }; }
  35. public IProjection AsProjection() { return _projection ?? Projections.Property(_property); }
  36. public ICriterion CreateCriterion(Func<string, ICriterion> stringFunc, Func<IProjection, ICriterion> projectionFunc)
  37. {
  38. return (_property != null)
  39. ? stringFunc(_property)
  40. : projectionFunc(_projection);
  41. }
  42. public ICriterion CreateCriterion(Func<string, object, ICriterion> stringFunc, Func<IProjection, object, ICriterion> projectionFunc, object value)
  43. {
  44. return (_property != null)
  45. ? stringFunc(_property, value)
  46. : projectionFunc(_projection, value);
  47. }
  48. public ICriterion CreateCriterion(ProjectionInfo rhs,
  49. Func<string, string, ICriterion> ssFunc,
  50. Func<string, IProjection, ICriterion> spFunc,
  51. Func<IProjection, string, ICriterion> psFunc,
  52. Func<IProjection, IProjection, ICriterion> ppFunc)
  53. {
  54. if (_property != null && rhs._property != null)
  55. return ssFunc(_property, rhs._property);
  56. if (_property != null)
  57. return spFunc(_property, rhs._projection);
  58. if (rhs._property != null)
  59. return psFunc(_projection, rhs._property);
  60. return ppFunc(_projection, rhs._projection);
  61. }
  62. public T Create<T>(Func<string, T> stringFunc, Func<IProjection, T> projectionFunc)
  63. {
  64. return (_property != null)
  65. ? stringFunc(_property)
  66. : projectionFunc(_projection);
  67. }
  68. public Order CreateOrder(Func<string, Order> orderStringDelegate, Func<IProjection, Order> orderProjectionDelegate)
  69. {
  70. return (_property != null)
  71. ? orderStringDelegate(_property)
  72. : orderProjectionDelegate(_projection);
  73. }
  74. /// <summary>
  75. /// Retreive the property name from a supplied PropertyProjection
  76. /// Note: throws is the supplied IProjection is not a PropertyProjection
  77. /// </summary>
  78. public string AsProperty()
  79. {
  80. if (_property != null) return _property;
  81. var propertyProjection = _projection as PropertyProjection;
  82. if (propertyProjection == null) throw new Exception("Cannot determine property for " + _projection);
  83. return propertyProjection.PropertyName;
  84. }
  85. }
  86. private readonly static IDictionary<ExpressionType, Func<ProjectionInfo, object, ICriterion>> _simpleExpressionCreators;
  87. private readonly static IDictionary<ExpressionType, Func<ProjectionInfo, ProjectionInfo, ICriterion>> _propertyExpressionCreators;
  88. private readonly static IDictionary<LambdaSubqueryType, IDictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>> _subqueryExpressionCreatorTypes;
  89. private readonly static IDictionary<string, Func<MethodCallExpression, ICriterion>> _customMethodCallProcessors;
  90. private readonly static IDictionary<string, Func<Expression, IProjection>> _customProjectionProcessors;
  91. static ExpressionProcessor()
  92. {
  93. _simpleExpressionCreators = new Dictionary<ExpressionType, Func<ProjectionInfo, object, ICriterion>>();
  94. _simpleExpressionCreators[ExpressionType.Equal] = Eq;
  95. _simpleExpressionCreators[ExpressionType.NotEqual] = Ne;
  96. _simpleExpressionCreators[ExpressionType.GreaterThan] = Gt;
  97. _simpleExpressionCreators[ExpressionType.GreaterThanOrEqual] = Ge;
  98. _simpleExpressionCreators[ExpressionType.LessThan] = Lt;
  99. _simpleExpressionCreators[ExpressionType.LessThanOrEqual] = Le;
  100. _propertyExpressionCreators = new Dictionary<ExpressionType, Func<ProjectionInfo, ProjectionInfo, ICriterion>>();
  101. _propertyExpressionCreators[ExpressionType.Equal] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.EqProperty, Restrictions.EqProperty, Restrictions.EqProperty, Restrictions.EqProperty);
  102. _propertyExpressionCreators[ExpressionType.NotEqual] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.NotEqProperty, Restrictions.NotEqProperty, Restrictions.NotEqProperty, Restrictions.NotEqProperty);
  103. _propertyExpressionCreators[ExpressionType.GreaterThan] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.GtProperty, Restrictions.GtProperty, Restrictions.GtProperty, Restrictions.GtProperty);
  104. _propertyExpressionCreators[ExpressionType.GreaterThanOrEqual] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.GeProperty, Restrictions.GeProperty, Restrictions.GeProperty, Restrictions.GeProperty);
  105. _propertyExpressionCreators[ExpressionType.LessThan] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.LtProperty, Restrictions.LtProperty, Restrictions.LtProperty, Restrictions.LtProperty);
  106. _propertyExpressionCreators[ExpressionType.LessThanOrEqual] = (lhs, rhs) => lhs.CreateCriterion(rhs, Restrictions.LeProperty, Restrictions.LeProperty, Restrictions.LeProperty, Restrictions.LeProperty);
  107. _subqueryExpressionCreatorTypes = new Dictionary<LambdaSubqueryType, IDictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>>();
  108. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact] = new Dictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>();
  109. _subqueryExpressionCreatorTypes[LambdaSubqueryType.All] = new Dictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>();
  110. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Some] = new Dictionary<ExpressionType, Func<string, DetachedCriteria, AbstractCriterion>>();
  111. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.Equal] = Subqueries.PropertyEq;
  112. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.NotEqual] = Subqueries.PropertyNe;
  113. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.GreaterThan] = Subqueries.PropertyGt;
  114. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.GreaterThanOrEqual] = Subqueries.PropertyGe;
  115. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.LessThan] = Subqueries.PropertyLt;
  116. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Exact][ExpressionType.LessThanOrEqual] = Subqueries.PropertyLe;
  117. _subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.Equal] = Subqueries.PropertyEqAll;
  118. _subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.GreaterThan] = Subqueries.PropertyGtAll;
  119. _subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.GreaterThanOrEqual] = Subqueries.PropertyGeAll;
  120. _subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.LessThan] = Subqueries.PropertyLtAll;
  121. _subqueryExpressionCreatorTypes[LambdaSubqueryType.All][ExpressionType.LessThanOrEqual] = Subqueries.PropertyLeAll;
  122. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.GreaterThan] = Subqueries.PropertyGtSome;
  123. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.GreaterThanOrEqual] = Subqueries.PropertyGeSome;
  124. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.LessThan] = Subqueries.PropertyLtSome;
  125. _subqueryExpressionCreatorTypes[LambdaSubqueryType.Some][ExpressionType.LessThanOrEqual] = Subqueries.PropertyLeSome;
  126. _customMethodCallProcessors = new Dictionary<string, Func<MethodCallExpression, ICriterion>>();
  127. RegisterCustomMethodCall(() => RestrictionExtensions.IsLike("", ""), RestrictionExtensions.ProcessIsLike);
  128. RegisterCustomMethodCall(() => RestrictionExtensions.IsLike("", "", null), RestrictionExtensions.ProcessIsLikeMatchMode);
  129. RegisterCustomMethodCall(() => RestrictionExtensions.IsLike("", "", null, null), RestrictionExtensions.ProcessIsLikeMatchModeEscapeChar);
  130. RegisterCustomMethodCall(() => RestrictionExtensions.IsInsensitiveLike("", ""), RestrictionExtensions.ProcessIsInsensitiveLike);
  131. RegisterCustomMethodCall(() => RestrictionExtensions.IsInsensitiveLike("", "", null), RestrictionExtensions.ProcessIsInsensitiveLikeMatchMode);
  132. RegisterCustomMethodCall(() => RestrictionExtensions.IsIn(null, new object[0]), RestrictionExtensions.ProcessIsInArray);
  133. RegisterCustomMethodCall(() => RestrictionExtensions.IsIn(null, new List<object>()), RestrictionExtensions.ProcessIsInCollection);
  134. RegisterCustomMethodCall(() => RestrictionExtensions.IsBetween(null, null).And(null), RestrictionExtensions.ProcessIsBetween);
  135. _customProjectionProcessors = new Dictionary<string, Func<Expression, IProjection>>();
  136. RegisterCustomProjection(() => default(DateTime).Year, e => ProjectionsExtensions.ProcessYear(e.Expression));
  137. RegisterCustomProjection(() => default(DateTime).Day, e => ProjectionsExtensions.ProcessDay(e.Expression));
  138. RegisterCustomProjection(() => default(DateTime).Month, e => ProjectionsExtensions.ProcessMonth(e.Expression));
  139. RegisterCustomProjection(() => default(DateTime).Hour, e => ProjectionsExtensions.ProcessHour(e.Expression));
  140. RegisterCustomProjection(() => default(DateTime).Minute, e => ProjectionsExtensions.ProcessMinute(e.Expression));
  141. RegisterCustomProjection(() => default(DateTime).Second, e => ProjectionsExtensions.ProcessSecond(e.Expression));
  142. RegisterCustomProjection(() => default(DateTime).Date, e => ProjectionsExtensions.ProcessDate(e.Expression));
  143. RegisterCustomProjection(() => default(DateTimeOffset).Year, e => ProjectionsExtensions.ProcessYear(e.Expression));
  144. RegisterCustomProjection(() => default(DateTimeOffset).Day, e => ProjectionsExtensions.ProcessDay(e.Expression));
  145. RegisterCustomProjection(() => default(DateTimeOffset).Month, e => ProjectionsExtensions.ProcessMonth(e.Expression));
  146. RegisterCustomProjection(() => default(DateTimeOffset).Hour, e => ProjectionsExtensions.ProcessHour(e.Expression));
  147. RegisterCustomProjection(() => default(DateTimeOffset).Minute, e => ProjectionsExtensions.ProcessMinute(e.Expression));
  148. RegisterCustomProjection(() => default(DateTimeOffset).Second, e => ProjectionsExtensions.ProcessSecond(e.Expression));
  149. RegisterCustomProjection(() => default(DateTimeOffset).Date, e => ProjectionsExtensions.ProcessDate(e.Expression));
  150. RegisterCustomProjection(() => ProjectionsExtensions.YearPart(default(DateTime)), e => ProjectionsExtensions.ProcessYear(e.Arguments[0]));
  151. RegisterCustomProjection(() => ProjectionsExtensions.DayPart(default(DateTime)), e => ProjectionsExtensions.ProcessDay(e.Arguments[0]));
  152. RegisterCustomProjection(() => ProjectionsExtensions.MonthPart(default(DateTime)), e => ProjectionsExtensions.ProcessMonth(e.Arguments[0]));
  153. RegisterCustomProjection(() => ProjectionsExtensions.HourPart(default(DateTime)), e => ProjectionsExtensions.ProcessHour(e.Arguments[0]));
  154. RegisterCustomProjection(() => ProjectionsExtensions.MinutePart(default(DateTime)), e => ProjectionsExtensions.ProcessMinute(e.Arguments[0]));
  155. RegisterCustomProjection(() => ProjectionsExtensions.SecondPart(default(DateTime)), e => ProjectionsExtensions.ProcessSecond(e.Arguments[0]));
  156. RegisterCustomProjection(() => ProjectionsExtensions.DatePart(default(DateTime)), e => ProjectionsExtensions.ProcessDate(e.Arguments[0]));
  157. RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(int)), ProjectionsExtensions.ProcessSqrt);
  158. RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(double)), ProjectionsExtensions.ProcessSqrt);
  159. RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(decimal)), ProjectionsExtensions.ProcessSqrt);
  160. RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(byte)), ProjectionsExtensions.ProcessSqrt);
  161. RegisterCustomProjection(() => ProjectionsExtensions.Sqrt(default(long)), ProjectionsExtensions.ProcessSqrt);
  162. RegisterCustomProjection(() => ProjectionsExtensions.Lower(string.Empty), ProjectionsExtensions.ProcessLower);
  163. RegisterCustomProjection(() => ProjectionsExtensions.Upper(string.Empty), ProjectionsExtensions.ProcessUpper);
  164. RegisterCustomProjection(() => ProjectionsExtensions.TrimStr(string.Empty), ProjectionsExtensions.ProcessTrimStr);
  165. RegisterCustomProjection(() => ProjectionsExtensions.StrLength(string.Empty), ProjectionsExtensions.ProcessStrLength);
  166. RegisterCustomProjection(() => ProjectionsExtensions.BitLength(string.Empty), ProjectionsExtensions.ProcessBitLength);
  167. RegisterCustomProjection(() => ProjectionsExtensions.Substr(string.Empty, 0, 0), ProjectionsExtensions.ProcessSubstr);
  168. RegisterCustomProjection(() => ProjectionsExtensions.CharIndex(string.Empty, string.Empty, 0), ProjectionsExtensions.ProcessCharIndex);
  169. RegisterCustomProjection(() => ProjectionsExtensions.Coalesce<DBNull>(null, null), ProjectionsExtensions.ProcessCoalesce);
  170. RegisterCustomProjection(() => ProjectionsExtensions.Coalesce<int>(null, 0), ProjectionsExtensions.ProcessCoalesce);
  171. RegisterCustomProjection(() => Projections.Concat(null), Projections.ProcessConcat);
  172. RegisterCustomProjection(() => ProjectionsExtensions.Mod(0, 0), ProjectionsExtensions.ProcessMod);
  173. RegisterCustomProjection(() => ProjectionsExtensions.Abs(default(int)), ProjectionsExtensions.ProcessIntAbs);
  174. RegisterCustomProjection(() => ProjectionsExtensions.Abs(default(double)), ProjectionsExtensions.ProcessDoubleAbs);
  175. RegisterCustomProjection(() => ProjectionsExtensions.Abs(default(Int64)), ProjectionsExtensions.ProcessInt64Abs);
  176. }
  177. private static ICriterion Eq(ProjectionInfo property, object value)
  178. {
  179. return property.CreateCriterion(Restrictions.Eq, Restrictions.Eq, value);
  180. }
  181. private static ICriterion Ne(ProjectionInfo property, object value)
  182. {
  183. return
  184. Restrictions.Not(
  185. property.CreateCriterion(Restrictions.Eq, Restrictions.Eq, value));
  186. }
  187. private static ICriterion Gt(ProjectionInfo property, object value)
  188. {
  189. return property.CreateCriterion(Restrictions.Gt, Restrictions.Gt, value);
  190. }
  191. private static ICriterion Ge(ProjectionInfo property, object value)
  192. {
  193. return property.CreateCriterion(Restrictions.Ge, Restrictions.Ge, value);
  194. }
  195. private static ICriterion Lt(ProjectionInfo property, object value)
  196. {
  197. return property.CreateCriterion(Restrictions.Lt, Restrictions.Lt, value);
  198. }
  199. private static ICriterion Le(ProjectionInfo property, object value)
  200. {
  201. return property.CreateCriterion(Restrictions.Le, Restrictions.Le, value);
  202. }
  203. /// <summary>
  204. /// Invoke the expression to extract its runtime value
  205. /// </summary>
  206. public static object FindValue(Expression expression)
  207. {
  208. var valueExpression = Expression.Lambda(expression).Compile();
  209. object value = valueExpression.DynamicInvoke();
  210. return value;
  211. }
  212. /// <summary>
  213. /// Retrieves the projection for the expression
  214. /// </summary>
  215. public static ProjectionInfo FindMemberProjection(Expression expression)
  216. {
  217. if (!IsMemberExpression(expression))
  218. return ProjectionInfo.ForProjection(Projections.Constant(FindValue(expression)));
  219. var unaryExpression = expression as UnaryExpression;
  220. if (unaryExpression != null)
  221. {
  222. if (!IsConversion(unaryExpression.NodeType))
  223. throw new Exception("Cannot interpret member from " + expression);
  224. return FindMemberProjection(unaryExpression.Operand);
  225. }
  226. var methodCallExpression = expression as MethodCallExpression;
  227. if (methodCallExpression != null)
  228. {
  229. var signature = Signature(methodCallExpression.Method);
  230. Func<Expression, IProjection> processor;
  231. if (_customProjectionProcessors.TryGetValue(signature, out processor))
  232. {
  233. return ProjectionInfo.ForProjection(processor(methodCallExpression));
  234. }
  235. }
  236. var memberExpression = expression as MemberExpression;
  237. if (memberExpression != null)
  238. {
  239. var signature = Signature(memberExpression.Member);
  240. Func<Expression, IProjection> processor;
  241. if (_customProjectionProcessors.TryGetValue(signature, out processor))
  242. {
  243. return ProjectionInfo.ForProjection(processor(memberExpression));
  244. }
  245. }
  246. return ProjectionInfo.ForProperty(FindMemberExpression(expression));
  247. }
  248. /// <summary>
  249. /// Retrieves the name of the property from a member expression
  250. /// </summary>
  251. /// <param name="expression">An expression tree that can contain either a member, or a conversion from a member.
  252. /// If the member is referenced from a null valued object, then the container is treated as an alias.</param>
  253. /// <returns>The name of the member property</returns>
  254. public static string FindMemberExpression(Expression expression)
  255. {
  256. var memberExpression = expression as MemberExpression;
  257. if (memberExpression != null)
  258. {
  259. if (memberExpression.Expression.NodeType == ExpressionType.MemberAccess
  260. || memberExpression.Expression.NodeType == ExpressionType.Call)
  261. {
  262. if (memberExpression.Member.DeclaringType.IsNullable())
  263. {
  264. // it's a Nullable<T>, so ignore any .Value
  265. if (memberExpression.Member.Name == "Value")
  266. return FindMemberExpression(memberExpression.Expression);
  267. }
  268. return FindMemberExpression(memberExpression.Expression) + "." + memberExpression.Member.Name;
  269. }
  270. if (IsConversion(memberExpression.Expression.NodeType))
  271. {
  272. return (FindMemberExpression(memberExpression.Expression) + "." + memberExpression.Member.Name).TrimStart('.');
  273. }
  274. return memberExpression.Member.Name;
  275. }
  276. var unaryExpression = expression as UnaryExpression;
  277. if (unaryExpression != null)
  278. {
  279. if (!IsConversion(unaryExpression.NodeType))
  280. throw new Exception("Cannot interpret member from " + expression);
  281. return FindMemberExpression(unaryExpression.Operand);
  282. }
  283. var methodCallExpression = expression as MethodCallExpression;
  284. if (methodCallExpression != null)
  285. {
  286. if (methodCallExpression.Method.Name == "GetType")
  287. return ClassMember(methodCallExpression.Object);
  288. if (methodCallExpression.Method.Name == "get_Item")
  289. return FindMemberExpression(methodCallExpression.Object);
  290. if (methodCallExpression.Method.Name == "First")
  291. return FindMemberExpression(methodCallExpression.Arguments[0]);
  292. throw new Exception("Unrecognised method call in expression " + methodCallExpression);
  293. }
  294. if (expression is ParameterExpression)
  295. return "";
  296. throw new Exception("Could not determine member from " + expression);
  297. }
  298. /// <summary>
  299. /// Retrieves the name of the property from a member expression (without leading member access)
  300. /// </summary>
  301. public static string FindPropertyExpression(Expression expression)
  302. {
  303. string memberExpression = FindMemberExpression(expression);
  304. int periodPosition = memberExpression.LastIndexOf('.') + 1;
  305. string property = (periodPosition <= 0) ? memberExpression : memberExpression.Substring(periodPosition);
  306. return property;
  307. }
  308. /// <summary>
  309. /// Retrieves a detached criteria from an appropriate lambda expression
  310. /// </summary>
  311. /// <param name="expression">Expresson for detached criteria using .As&lt;>() extension"/></param>
  312. /// <returns>Evaluated detached criteria</returns>
  313. public static DetachedCriteria FindDetachedCriteria(Expression expression)
  314. {
  315. var methodCallExpression = expression as MethodCallExpression;
  316. if (methodCallExpression == null)
  317. throw new Exception("right operand should be detachedQueryInstance.As<T>() - " + expression);
  318. var criteriaExpression = Expression.Lambda(methodCallExpression.Object).Compile();
  319. QueryOver detachedQuery = (QueryOver)criteriaExpression.DynamicInvoke();
  320. return detachedQuery.DetachedCriteria;
  321. }
  322. private static bool EvaluatesToNull(Expression expression)
  323. {
  324. var valueExpression = Expression.Lambda(expression).Compile();
  325. object value = valueExpression.DynamicInvoke();
  326. return (value == null);
  327. }
  328. private static System.Type FindMemberType(Expression expression)
  329. {
  330. var memberExpression = expression as MemberExpression;
  331. if (memberExpression != null)
  332. {
  333. return memberExpression.Type;
  334. }
  335. var unaryExpression = expression as UnaryExpression;
  336. if (unaryExpression != null)
  337. {
  338. if (!IsConversion(unaryExpression.NodeType))
  339. throw new Exception("Cannot interpret member from " + expression);
  340. return FindMemberType(unaryExpression.Operand);
  341. }
  342. var methodCallExpression = expression as MethodCallExpression;
  343. if (methodCallExpression != null)
  344. {
  345. return methodCallExpression.Method.ReturnType;
  346. }
  347. throw new Exception("Could not determine member type from " + expression);
  348. }
  349. private static bool IsMemberExpression(Expression expression)
  350. {
  351. if (expression is ParameterExpression)
  352. return true;
  353. var memberExpression = expression as MemberExpression;
  354. if (memberExpression != null)
  355. {
  356. if (memberExpression.Expression == null)
  357. return false; // it's a member of a static class
  358. if (IsMemberExpression(memberExpression.Expression))
  359. return true;
  360. // if the member has a null value, it was an alias
  361. return EvaluatesToNull(memberExpression.Expression);
  362. }
  363. var unaryExpression = expression as UnaryExpression;
  364. if (unaryExpression != null)
  365. {
  366. if (!IsConversion(unaryExpression.NodeType))
  367. throw new Exception("Cannot interpret member from " + expression);
  368. return IsMemberExpression(unaryExpression.Operand);
  369. }
  370. var methodCallExpression = expression as MethodCallExpression;
  371. if (methodCallExpression != null)
  372. {
  373. string signature = Signature(methodCallExpression.Method);
  374. if (_customProjectionProcessors.ContainsKey(signature))
  375. return true;
  376. if (methodCallExpression.Method.Name == "First")
  377. {
  378. if (IsMemberExpression(methodCallExpression.Arguments[0]))
  379. return true;
  380. return EvaluatesToNull(methodCallExpression.Arguments[0]);
  381. }
  382. if (methodCallExpression.Method.Name == "GetType"
  383. || methodCallExpression.Method.Name == "get_Item")
  384. {
  385. if (IsMemberExpression(methodCallExpression.Object))
  386. return true;
  387. return EvaluatesToNull(methodCallExpression.Object);
  388. }
  389. }
  390. return false;
  391. }
  392. private static bool IsConversion(ExpressionType expressionType)
  393. {
  394. return (expressionType == ExpressionType.Convert || expressionType == ExpressionType.ConvertChecked);
  395. }
  396. private static object ConvertType(object value, System.Type type)
  397. {
  398. if (value == null)
  399. return null;
  400. if (type.IsInstanceOfType(value))
  401. return value;
  402. type = type.UnwrapIfNullable();
  403. if (type.IsEnum)
  404. return Enum.ToObject(type, value);
  405. if (type.IsPrimitive)
  406. return Convert.ChangeType(value, type);
  407. throw new Exception(string.Format("Cannot convert '{0}' to {1}", value, type));
  408. }
  409. private static ICriterion ProcessSimpleExpression(BinaryExpression be)
  410. {
  411. if (be.Left.NodeType == ExpressionType.Call && ((MethodCallExpression)be.Left).Method.Name == "CompareString")
  412. return ProcessVisualBasicStringComparison(be);
  413. return ProcessSimpleExpression(be.Left, be.Right, be.NodeType);
  414. }
  415. private static ICriterion ProcessSimpleExpression(Expression left, Expression right, ExpressionType nodeType)
  416. {
  417. ProjectionInfo property = FindMemberProjection(left);
  418. System.Type propertyType = FindMemberType(left);
  419. object value = FindValue(right);
  420. value = ConvertType(value, propertyType);
  421. if (value == null)
  422. return ProcessSimpleNullExpression(property, nodeType);
  423. Func<ProjectionInfo, object, ICriterion> simpleExpressionCreator;
  424. if (!_simpleExpressionCreators.TryGetValue(nodeType, out simpleExpressionCreator))
  425. throw new Exception("Unhandled simple expression type: " + nodeType);
  426. return simpleExpressionCreator(property, value);
  427. }
  428. private static ICriterion ProcessVisualBasicStringComparison(BinaryExpression be)
  429. {
  430. var methodCall = (MethodCallExpression)be.Left;
  431. if (IsMemberExpression(methodCall.Arguments[1]))
  432. return ProcessMemberExpression(methodCall.Arguments[0], methodCall.Arguments[1], be.NodeType);
  433. else
  434. return ProcessSimpleExpression(methodCall.Arguments[0], methodCall.Arguments[1], be.NodeType);
  435. }
  436. private static ICriterion ProcessSimpleNullExpression(ProjectionInfo property, ExpressionType expressionType)
  437. {
  438. if (expressionType == ExpressionType.Equal)
  439. return property.CreateCriterion(Restrictions.IsNull, Restrictions.IsNull);
  440. if (expressionType == ExpressionType.NotEqual)
  441. return Restrictions.Not(
  442. property.CreateCriterion(Restrictions.IsNull, Restrictions.IsNull));
  443. throw new Exception("Cannot supply null value to operator " + expressionType);
  444. }
  445. private static ICriterion ProcessMemberExpression(BinaryExpression be)
  446. {
  447. return ProcessMemberExpression(be.Left, be.Right, be.NodeType);
  448. }
  449. private static ICriterion ProcessMemberExpression(Expression left, Expression right, ExpressionType nodeType)
  450. {
  451. ProjectionInfo leftProperty = FindMemberProjection(left);
  452. ProjectionInfo rightProperty = FindMemberProjection(right);
  453. Func<ProjectionInfo, ProjectionInfo, ICriterion> propertyExpressionCreator;
  454. if (!_propertyExpressionCreators.TryGetValue(nodeType, out propertyExpressionCreator))
  455. throw new Exception("Unhandled property expression type: " + nodeType);
  456. return propertyExpressionCreator(leftProperty, rightProperty);
  457. }
  458. private static ICriterion ProcessAndExpression(BinaryExpression expression)
  459. {
  460. return Restrictions.And(
  461. ProcessExpression(expression.Left),
  462. ProcessExpression(expression.Right));
  463. }
  464. private static ICriterion ProcessOrExpression(BinaryExpression expression)
  465. {
  466. return Restrictions.Or(
  467. ProcessExpression(expression.Left),
  468. ProcessExpression(expression.Right));
  469. }
  470. private static ICriterion ProcessBinaryExpression(BinaryExpression expression)
  471. {
  472. switch (expression.NodeType)
  473. {
  474. case ExpressionType.AndAlso:
  475. return ProcessAndExpression(expression);
  476. case ExpressionType.OrElse:
  477. return ProcessOrExpression(expression);
  478. case ExpressionType.Equal:
  479. case ExpressionType.NotEqual:
  480. case ExpressionType.GreaterThan:
  481. case ExpressionType.GreaterThanOrEqual:
  482. case ExpressionType.LessThan:
  483. case ExpressionType.LessThanOrEqual:
  484. if (IsMemberExpression(expression.Right))
  485. return ProcessMemberExpression(expression);
  486. else
  487. return ProcessSimpleExpression(expression);
  488. default:
  489. throw new Exception("Unhandled binary expression: " + expression.NodeType + ", " + expression);
  490. }
  491. }
  492. private static ICriterion ProcessBooleanExpression(Expression expression)
  493. {
  494. if (expression is MemberExpression)
  495. {
  496. return Restrictions.Eq(FindMemberExpression(expression), true);
  497. }
  498. var unaryExpression = expression as UnaryExpression;
  499. if (unaryExpression != null)
  500. {
  501. if (unaryExpression.NodeType != ExpressionType.Not)
  502. throw new Exception("Cannot interpret member from " + expression);
  503. if (IsMemberExpression(unaryExpression.Operand))
  504. return Restrictions.Eq(FindMemberExpression(unaryExpression.Operand), false);
  505. else
  506. return Restrictions.Not(ProcessExpression(unaryExpression.Operand));
  507. }
  508. var methodCallExpression = expression as MethodCallExpression;
  509. if (methodCallExpression != null)
  510. {
  511. return ProcessCustomMethodCall(methodCallExpression);
  512. }
  513. var typeBinaryExpression = expression as TypeBinaryExpression;
  514. if (typeBinaryExpression != null)
  515. {
  516. return Restrictions.Eq(ClassMember(typeBinaryExpression.Expression), typeBinaryExpression.TypeOperand.FullName);
  517. }
  518. throw new Exception("Could not determine member type from " + expression.NodeType + ", " + expression + ", " + expression.GetType());
  519. }
  520. private static string ClassMember(Expression expression)
  521. {
  522. if (expression.NodeType == ExpressionType.MemberAccess)
  523. return FindMemberExpression(expression) + ".class";
  524. return "class";
  525. }
  526. public static string Signature(MethodInfo methodInfo)
  527. {
  528. while (methodInfo.IsGenericMethod && !methodInfo.IsGenericMethodDefinition)
  529. methodInfo = methodInfo.GetGenericMethodDefinition();
  530. return methodInfo.DeclaringType.FullName
  531. + ":" + methodInfo;
  532. }
  533. public static string Signature(MemberInfo memberInfo)
  534. {
  535. return memberInfo.DeclaringType.FullName + ":" + memberInfo;
  536. }
  537. private static ICriterion ProcessCustomMethodCall(MethodCallExpression methodCallExpression)
  538. {
  539. string signature = Signature(methodCallExpression.Method);
  540. Func<MethodCallExpression, ICriterion> customMethodCallProcessor;
  541. if (!_customMethodCallProcessors.TryGetValue(signature, out customMethodCallProcessor))
  542. throw new Exception("Unrecognised method call: " + signature);
  543. return customMethodCallProcessor(methodCallExpression);
  544. }
  545. private static ICriterion ProcessExpression(Expression expression)
  546. {
  547. var binaryExpression = expression as BinaryExpression;
  548. if (binaryExpression != null)
  549. return ProcessBinaryExpression(binaryExpression);
  550. return ProcessBooleanExpression(expression);
  551. }
  552. private static ICriterion ProcessLambdaExpression(LambdaExpression expression)
  553. {
  554. return ProcessExpression(expression.Body);
  555. }
  556. /// <summary>
  557. /// Convert a lambda expression to NHibernate ICriterion
  558. /// </summary>
  559. /// <typeparam name="T">The type of the lambda expression</typeparam>
  560. /// <param name="expression">The lambda expression to convert</param>
  561. /// <returns>NHibernate ICriterion</returns>
  562. public static ICriterion ProcessExpression<T>(Expression<Func<T, bool>> expression)
  563. {
  564. return ProcessLambdaExpression(expression);
  565. }
  566. /// <summary>
  567. /// Convert a lambda expression to NHibernate ICriterion
  568. /// </summary>
  569. /// <param name="expression">The lambda expression to convert</param>
  570. /// <returns>NHibernate ICriterion</returns>
  571. public static ICriterion ProcessExpression(Expression<Func<bool>> expression)
  572. {
  573. return ProcessLambdaExpression(expression);
  574. }
  575. /// <summary>
  576. /// Convert a lambda expression to NHibernate Order
  577. /// </summary>
  578. /// <typeparam name="T">The type of the lambda expression</typeparam>
  579. /// <param name="expression">The lambda expression to convert</param>
  580. /// <param name="orderDelegate">The appropriate order delegate (order direction)</param>
  581. /// <returns>NHibernate Order</returns>
  582. public static Order ProcessOrder<T>(Expression<Func<T, object>> expression,
  583. Func<string, Order> orderDelegate)
  584. {
  585. string property = FindMemberExpression(expression.Body);
  586. Order order = orderDelegate(property);
  587. return order;
  588. }
  589. /// <summary>
  590. /// Convert a lambda expression to NHibernate Order
  591. /// </summary>
  592. /// <param name="expression">The lambda expression to convert</param>
  593. /// <param name="orderDelegate">The appropriate order delegate (order direction)</param>
  594. /// <returns>NHibernate Order</returns>
  595. public static Order ProcessOrder(Expression<Func<object>> expression,
  596. Func<string, Order> orderDelegate)
  597. {
  598. string property = FindMemberExpression(expression.Body);
  599. Order order = orderDelegate(property);
  600. return order;
  601. }
  602. /// <summary>
  603. /// Convert a lambda expression to NHibernate Order
  604. /// </summary>
  605. /// <param name="expression">The lambda expression to convert</param>
  606. /// <param name="orderDelegate">The appropriate order delegate (order direction)</param>
  607. /// <returns>NHibernate Order</returns>
  608. public static Order ProcessOrder( LambdaExpression expression,
  609. Func<string, Order> orderDelegate)
  610. {
  611. string property = FindPropertyExpression(expression.Body);
  612. Order order = orderDelegate(property);
  613. return order;
  614. }
  615. /// <summary>
  616. /// Convert a lambda expression to NHibernate Order
  617. /// </summary>
  618. /// <param name="expression">The lambda expression to convert</param>
  619. /// <param name="orderStringDelegate">The appropriate order delegate (order direction)</param>
  620. /// <param name="orderProjectionDelegate">The appropriate order delegate (order direction)</param>
  621. /// <returns>NHibernate Order</returns>
  622. public static Order ProcessOrder( LambdaExpression expression,
  623. Func<string, Order> orderStringDelegate,
  624. Func<IProjection, Order> orderProjectionDelegate)
  625. {
  626. ProjectionInfo projection = FindMemberProjection(expression.Body);
  627. Order order = projection.CreateOrder(orderStringDelegate, orderProjectionDelegate);
  628. return order;
  629. }
  630. private static AbstractCriterion ProcessSubqueryExpression(LambdaSubqueryType subqueryType,
  631. BinaryExpression be)
  632. {
  633. string property = FindMemberExpression(be.Left);
  634. DetachedCriteria detachedCriteria = FindDetachedCriteria(be.Right);
  635. var subqueryExpressionCreators = _subqueryExpressionCreatorTypes[subqueryType];
  636. Func<string, DetachedCriteria, AbstractCriterion> subqueryExpressionCreator;
  637. if (!subqueryExpressionCreators.TryGetValue(be.NodeType, out subqueryExpressionCreator))
  638. throw new Exception("Unhandled subquery expression type: " + subqueryType + "," + be.NodeType);
  639. return subqueryExpressionCreator(property, detachedCriteria);
  640. }
  641. /// <summary>
  642. /// Convert a lambda expression to NHibernate subquery AbstractCriterion
  643. /// </summary>
  644. /// <typeparam name="T">type of member expression</typeparam>
  645. /// <param name="subqueryType">type of subquery</param>
  646. /// <param name="expression">lambda expression to convert</param>
  647. /// <returns>NHibernate.ICriterion.AbstractCriterion</returns>
  648. public static AbstractCriterion ProcessSubquery<T>(LambdaSubqueryType subqueryType,
  649. Expression<Func<T, bool>> expression)
  650. {
  651. BinaryExpression be = (BinaryExpression)expression.Body;
  652. AbstractCriterion criterion = ProcessSubqueryExpression(subqueryType, be);
  653. return criterion;
  654. }
  655. /// <summary>
  656. /// Convert a lambda expression to NHibernate subquery AbstractCriterion
  657. /// </summary>
  658. /// <param name="subqueryType">type of subquery</param>
  659. /// <param name="expression">lambda expression to convert</param>
  660. /// <returns>NHibernate.ICriterion.AbstractCriterion</returns>
  661. public static AbstractCriterion ProcessSubquery(LambdaSubqueryType subqueryType,
  662. Expression<Func<bool>> expression)
  663. {
  664. BinaryExpression be = (BinaryExpression)expression.Body;
  665. AbstractCriterion criterion = ProcessSubqueryExpression(subqueryType, be);
  666. return criterion;
  667. }
  668. /// <summary>
  669. /// Register a custom method for use in a QueryOver expression
  670. /// </summary>
  671. /// <param name="function">Lambda expression demonstrating call of custom method</param>
  672. /// <param name="functionProcessor">function to convert MethodCallExpression to ICriterion</param>
  673. public static void RegisterCustomMethodCall(Expression<Func<bool>> function, Func<MethodCallExpression, ICriterion> functionProcessor)
  674. {
  675. MethodCallExpression functionExpression = (MethodCallExpression)function.Body;
  676. string signature = Signature(functionExpression.Method);
  677. _customMethodCallProcessors.Add(signature, functionProcessor);
  678. }
  679. /// <summary>
  680. /// Register a custom projection for use in a QueryOver expression
  681. /// </summary>
  682. /// <param name="function">Lambda expression demonstrating call of custom method</param>
  683. /// <param name="functionProcessor">function to convert MethodCallExpression to IProjection</param>
  684. public static void RegisterCustomProjection<T>(Expression<Func<T>> function, Func<MethodCallExpression, IProjection> functionProcessor)
  685. {
  686. MethodCallExpression functionExpression = (MethodCallExpression)function.Body;
  687. string signature = Signature(functionExpression.Method);
  688. _customProjectionProcessors.Add(signature, e => functionProcessor((MethodCallExpression) e));
  689. }
  690. /// <summary>
  691. /// Register a custom projection for use in a QueryOver expression
  692. /// </summary>
  693. /// <param name="function">Lambda expression demonstrating call of custom method</param>
  694. /// <param name="functionProcessor">function to convert MethodCallExpression to IProjection</param>
  695. public static void RegisterCustomProjection<T>(Expression<Func<T>> function, Func<MemberExpression, IProjection> functionProcessor)
  696. {
  697. MemberExpression functionExpression = (MemberExpression) function.Body;
  698. string signature = Signature(functionExpression.Member);
  699. _customProjectionProcessors.Add(signature, e => functionProcessor((MemberExpression) e));
  700. }
  701. }
  702. }