PageRenderTime 67ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Compilation/ReflectExpressionBuilder.cs

#
C# | 215 lines | 99 code | 19 blank | 97 comment | 11 complexity | faa15030f2734633ca760b688098d46a MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <summary>
  3. // Implements an expression builder that provides a strongly-typed reference to a type or member info, instead of a string.
  4. // Targeted properties still receive a string as input, but the expression is validated during page compilation or execution.
  5. // If the supplied expression cannot be resolved to a type or member info from an assembly loaded into the current domain
  6. // then an exception is thrown. Note that type and member name resolution is case-sensitive.
  7. // Only public types and members can be resolved.
  8. // </summary>
  9. // --------------------------------------------------------------------------------------------------------------------
  10. namespace BlogEngine.Core.Compilation
  11. {
  12. using System;
  13. using System.CodeDom;
  14. using System.Linq;
  15. using System.Security.Permissions;
  16. using System.Web;
  17. using System.Web.Compilation;
  18. using System.Web.UI;
  19. /// <summary>
  20. /// Implements an expression builder that provides a strongly-typed reference to a type or member info, instead of a string.
  21. /// Targeted properties still receive a string as input, but the expression is validated during page compilation or execution.
  22. /// If the supplied expression cannot be resolved to a type or member info from an assembly loaded into the current domain
  23. /// then an exception is thrown. Note that type and member name resolution is case-sensitive.
  24. /// Only public types and members can be resolved.
  25. /// </summary>
  26. /// <remarks>
  27. /// &lt;add expressionPrefix="Reflect" type="WebApp.Compilation.ReflectExpressionBuilder, WebApp"/&gt;
  28. /// Add the above to the web.config.
  29. /// The following examples illustrate the usage of the expression in web pages:
  30. /// &lt;asp:ObjectDataSource SelectMethod="&lt;%$ Reflect: ComCard.Components.BusinessObjects.Task, Search %&gt;" ... /&gt;
  31. /// This will resolve the type ComCard.Components.BusinessObjects.Task, and validate that Search is a public member on that type.
  32. /// The member name will be returned for binding to the property.
  33. /// &lt;asp:ObjectDataSource DataObjectTypeName="&lt;%$ Reflect: ComCard.Components.BusinessObjects.LogEntry %&gt;" ... /&gt;
  34. /// This will resolve the type ComCard.Components.BusinessObjects.LogEntry. The type name will be returned for binding.
  35. /// </remarks>
  36. [ExpressionPrefix("Reflect")]
  37. [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  38. [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  39. public class ReflectExpressionBuilder : ExpressionBuilder
  40. {
  41. #region Properties
  42. /// <summary>
  43. /// Gets a flag that indicates whether the expression builder supports no-compile evaluation.
  44. /// Returns true, as the target type can be validated at runtime as well.
  45. /// </summary>
  46. public override bool SupportsEvaluate
  47. {
  48. get
  49. {
  50. return true;
  51. }
  52. }
  53. #endregion
  54. #region Public Methods
  55. /// <summary>
  56. /// Evaluates the expression at runtime.
  57. /// </summary>
  58. /// <param name="target">
  59. /// The target object.
  60. /// </param>
  61. /// <param name="entry">
  62. /// The entry for the property bound to the expression.
  63. /// </param>
  64. /// <param name="parsedData">
  65. /// The parsed expression data.
  66. /// </param>
  67. /// <param name="context">
  68. /// The current expression builder context.
  69. /// </param>
  70. /// <returns>
  71. /// A string representing the target type or member.
  72. /// </returns>
  73. public override object EvaluateExpression(
  74. object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
  75. {
  76. return parsedData;
  77. }
  78. /// <summary>
  79. /// Returns a <see cref="System.CodeDom"/> expression for invoking the expression from a
  80. /// compiled page at runtime.
  81. /// </summary>
  82. /// <param name="entry">
  83. /// The entry for the bound property.
  84. /// </param>
  85. /// <param name="parsedData">
  86. /// The parsed expression data.
  87. /// </param>
  88. /// <param name="context">
  89. /// The expression builder context.
  90. /// </param>
  91. /// <returns>
  92. /// A <see cref="CodeExpression"/> for invoking the expression.
  93. /// </returns>
  94. public override CodeExpression GetCodeExpression(
  95. BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
  96. {
  97. return new CodePrimitiveExpression(parsedData);
  98. }
  99. /// <summary>
  100. /// Parses and validates the expression data and returns a canonical type or member name,
  101. /// or throws an exception if the expression is invalid.
  102. /// </summary>
  103. /// <param name="expression">
  104. /// The raw expression to parse.
  105. /// </param>
  106. /// <param name="propertyType">
  107. /// The target property type.
  108. /// </param>
  109. /// <param name="context">
  110. /// Contextual information for the expression builder.
  111. /// </param>
  112. /// <returns>
  113. /// A string representing the target type or member name for binding.
  114. /// </returns>
  115. public override object ParseExpression(string expression, Type propertyType, ExpressionBuilderContext context)
  116. {
  117. var parsed = false;
  118. string typeName = null;
  119. string memberName = null;
  120. if (!String.IsNullOrEmpty(expression))
  121. {
  122. var parts = expression.Split(',');
  123. if (parts.Length > 0 && parts.Length < 3)
  124. {
  125. switch (parts.Length)
  126. {
  127. case 1:
  128. typeName = parts[0].Trim();
  129. break;
  130. case 2:
  131. typeName = parts[0].Trim();
  132. memberName = parts[1].Trim();
  133. break;
  134. }
  135. parsed = true;
  136. }
  137. }
  138. if (!parsed)
  139. {
  140. throw new HttpException(String.Format("Invalid Reflect expression - '{0}'.", expression));
  141. }
  142. // now validate the expression fields
  143. return ValidateExpression(typeName, memberName);
  144. }
  145. #endregion
  146. #region Methods
  147. /// <summary>
  148. /// Validates that the specified type and member name can be resolved in the current context.
  149. /// Member name resolution is optional.
  150. /// </summary>
  151. /// <param name="typeName">
  152. /// The full name of the type.
  153. /// </param>
  154. /// <param name="memberName">
  155. /// The member name to resolve, or null to ignore.
  156. /// </param>
  157. /// <returns>
  158. /// The type / member name as a string for binding to the target property.
  159. /// </returns>
  160. private static string ValidateExpression(string typeName, string memberName)
  161. {
  162. // resolve type name first
  163. Type resolvedType = null;
  164. foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
  165. {
  166. resolvedType = assembly.GetType(typeName, false, false);
  167. if (resolvedType != null)
  168. {
  169. break;
  170. }
  171. }
  172. // if type was not resolved then raise error
  173. if (resolvedType == null)
  174. {
  175. var message =
  176. String.Format(
  177. "Reflect Expression: Type '{0}' could not be resolved in the current context.", typeName);
  178. throw new HttpCompileException(message);
  179. }
  180. // resolve the member name if provided - don't care about multiple matches
  181. var bindingValue = typeName;
  182. if (!String.IsNullOrEmpty(memberName))
  183. {
  184. bindingValue = memberName;
  185. if (!resolvedType.GetMember(memberName).Any())
  186. {
  187. var message = String.Format(
  188. "Reflect Expression: Member '{0}' for type '{1}' does not exist.", memberName, typeName);
  189. throw new HttpCompileException(message);
  190. }
  191. }
  192. return bindingValue;
  193. }
  194. #endregion
  195. }
  196. }