Skip to content
Browse files

First cut of MVC Razor support in ServiceStack based on work done in …

…razorengine.codeplex.com

IContainerAdapter and JSONP tests and bug fixes
  • Loading branch information...
1 parent 39dcefe commit 5a8f2a1a110e69f14d162c17e2bcc754683013d4 @mythz mythz committed Jun 22, 2011
Showing with 4,891 additions and 406 deletions.
  1. +51 −0 src/RazorEngine/Compilation/CSharp/CSharpDirectCompilerService.cs
  2. +48 −0 src/RazorEngine/Compilation/CSharp/CSharpRazorCodeGenerator.cs
  3. +41 −0 src/RazorEngine/Compilation/CSharp/CSharpRazorCodeLanguage.cs
  4. +191 −0 src/RazorEngine/Compilation/CompilerServiceBase.cs
  5. +90 −0 src/RazorEngine/Compilation/CompilerServices.cs
  6. +33 −0 src/RazorEngine/Compilation/DefaultCompilerServiceFactory.cs
  7. +89 −0 src/RazorEngine/Compilation/DirectCompilerServiceBase.cs
  8. +12 −0 src/RazorEngine/Compilation/HasDynamicModelAttribute.cs
  9. +27 −0 src/RazorEngine/Compilation/ICompilerService.cs
  10. +21 −0 src/RazorEngine/Compilation/ICompilerServiceFactory.cs
  11. +56 −0 src/RazorEngine/Compilation/RazorDynamicObject.cs
  12. +49 −0 src/RazorEngine/Compilation/TypeContext.cs
  13. +68 −0 src/RazorEngine/Configuration/ConfigurationServices.cs
  14. +26 −0 src/RazorEngine/Configuration/NamespaceConfigurationElement.cs
  15. +36 −0 src/RazorEngine/Configuration/NamespaceConfigurationElementCollection.cs
  16. +71 −0 src/RazorEngine/Configuration/RazorEngineConfigurationSection.cs
  17. +92 −0 src/RazorEngine/Configuration/TemplateServiceConfigurationElement.cs
  18. +66 −0 src/RazorEngine/Configuration/TemplateServiceConfigurationElementCollection.cs
  19. +14 −0 src/RazorEngine/IRazorTemplate.cs
  20. +13 −0 src/RazorEngine/Language.cs
  21. +10 −0 src/RazorEngine/MicrosoftCSharpReferenceStub.cs
  22. +365 −0 src/RazorEngine/MvcRazorFormat.cs
  23. +22 −0 src/RazorEngine/Properties/AssemblyInfo.cs
  24. +237 −0 src/RazorEngine/Razor.cs
  25. +137 −0 src/RazorEngine/RazorEngine.csproj
  26. +48 −0 src/RazorEngine/RazorHandler.cs
  27. +127 −0 src/RazorEngine/RazorPage.cs
  28. +26 −0 src/RazorEngine/RazorPageBase.cs
  29. +25 −0 src/RazorEngine/Templating/DefaultActivator.cs
  30. +43 −0 src/RazorEngine/Templating/DelegateActivator.cs
  31. +40 −0 src/RazorEngine/Templating/DelegateTemplateResolver.cs
  32. +19 −0 src/RazorEngine/Templating/IActivator.cs
  33. +44 −0 src/RazorEngine/Templating/ITemplate.cs
  34. +16 −0 src/RazorEngine/Templating/ITemplateOfT.cs
  35. +17 −0 src/RazorEngine/Templating/ITemplateResolver.cs
  36. +36 −0 src/RazorEngine/Templating/RequireNamespacesAttribute.cs
  37. +136 −0 src/RazorEngine/Templating/TemplateBase.cs
  38. +50 −0 src/RazorEngine/Templating/TemplateBaseOfT.cs
  39. +33 −0 src/RazorEngine/Templating/TemplateCompilationException.cs
  40. +35 −0 src/RazorEngine/Templating/TemplateParsingException.cs
  41. +115 −0 src/RazorEngine/Templating/TemplateService.ServiceStack.cs
  42. +378 −0 src/RazorEngine/Templating/TemplateService.cs
  43. +66 −0 src/RazorEngine/Templating/TemplateServiceFactory.cs
  44. +44 −0 src/RazorEngine/Templating/TemplateWriter.cs
  45. +15 −0 src/ServiceStack.Common/ReflectionExtensions.cs
  46. +7 −0 src/ServiceStack.Common/ServiceClient.Web/WebRequestExtensions.cs
  47. +2 −0 src/ServiceStack.Common/Web/HttpHeaders.cs
  48. +7 −0 src/ServiceStack.ServiceInterface/ServiceBase.cs
  49. +10 −0 src/ServiceStack.sln
  50. +1 −1 src/ServiceStack/CacheAccess.Providers/ContentSerializer.cs
  51. +16 −10 src/ServiceStack/Funq/Container.cs
  52. +12 −0 src/ServiceStack/Markdown/ITemplatePage.cs
  53. +10 −10 src/ServiceStack/Markdown/MarkdownViewBase.cs
  54. +1 −1 src/ServiceStack/Markdown/TagBuilder.cs
  55. +1 −1 src/ServiceStack/Properties/AssemblyInfo.cs
  56. +1 −0 src/ServiceStack/ServiceHost/HttpRequestExtensions.cs
  57. +1 −0 src/ServiceStack/ServiceStack.csproj
  58. +34 −6 src/ServiceStack/WebHost.EndPoints/EndpointHostConfig.cs
  59. +28 −10 src/ServiceStack/WebHost.EndPoints/Extensions/IHttpResponseExtensions.cs
  60. +12 −6 src/ServiceStack/WebHost.EndPoints/Formats/MarkdownFormat.cs
  61. +6 −5 src/ServiceStack/WebHost.EndPoints/GenericHandler.cs
  62. +1 −1 src/ServiceStack/WebHost.EndPoints/Metadata/BaseSoapMetadataHandler.cs
  63. +1 −1 src/ServiceStack/WebHost.EndPoints/Metadata/CustomMetadataHandler.cs
  64. +1 −1 src/ServiceStack/WebHost.EndPoints/Metadata/JsonMetadataHandler.cs
  65. +1 −1 src/ServiceStack/WebHost.EndPoints/Metadata/JsvMetadataHandler.cs
  66. +1 −1 src/ServiceStack/WebHost.EndPoints/Metadata/XmlMetadataHandler.cs
  67. +5 −5 src/ServiceStack/WebHost.EndPoints/RestHandler.cs
  68. +2 −1 src/ServiceStack/WebHost.EndPoints/Support/HttpListenerBase.cs
  69. +32 −4 src/ServiceStack/WebHost.EndPoints/Support/Markdown/Evaluator.cs
  70. +10 −6 src/ServiceStack/WebHost.EndPoints/Support/Markdown/MarkdownPage.cs
  71. +1 −1 src/ServiceStack/WebHost.EndPoints/Support/Markdown/TemplateExtensions.cs
  72. +1 −1 src/ServiceStack/WebHost.EndPoints/Support/Markdown/TextBlock.cs
  73. +4 −15 src/ServiceStack/WebHost.EndPoints/Support/Metadata/Controls/IndexOperationsControl.cs
  74. +4 −9 src/ServiceStack/WebHost.EndPoints/Support/Metadata/Controls/OperationsControl.cs
  75. +11 −8 tests/ServiceStack.ServiceHost.Tests/App.config
  76. +19 −0 tests/ServiceStack.ServiceHost.Tests/AppData/NoTemplate/Static.cshtml
  77. +19 −0 tests/ServiceStack.ServiceHost.Tests/AppData/Template/StaticTpl.cshtml
  78. +20 −0 tests/ServiceStack.ServiceHost.Tests/AppData/Template/default.cshtml
  79. +0 −84 tests/ServiceStack.ServiceHost.Tests/AppData/TestsResults/AltCustomerDetailsResponse.htm
  80. +0 −31 tests/ServiceStack.ServiceHost.Tests/AppData/TestsResults/Customer.htm
  81. +0 −84 tests/ServiceStack.ServiceHost.Tests/AppData/TestsResults/CustomerDetailsResponse.htm
  82. +0 −83 tests/ServiceStack.ServiceHost.Tests/AppData/TestsResults/CustomerDetailsResponse.txt
  83. +2 −2 tests/ServiceStack.ServiceHost.Tests/Formats/IntroductionLayoutTests.cs
  84. +3 −1 tests/ServiceStack.ServiceHost.Tests/Formats/MockClass.cs
  85. +1 −2 tests/ServiceStack.ServiceHost.Tests/Formats/TemplateTests.cs
  86. +6 −4 tests/ServiceStack.ServiceHost.Tests/Formats/ViewTests.cs
  87. +11 −0 tests/ServiceStack.ServiceHost.Tests/Formats_Razor/CustomRazorBasePage.cs
  88. +108 −0 tests/ServiceStack.ServiceHost.Tests/Formats_Razor/RazorEngineTests.cs
  89. +989 −0 tests/ServiceStack.ServiceHost.Tests/Formats_Razor/TemplateTests.cs
  90. +22 −2 tests/ServiceStack.ServiceHost.Tests/ServiceStack.ServiceHost.Tests.csproj
  91. +18 −0 tests/ServiceStack.ServiceHost.Tests/Views/NoTemplate/Dynamic.cshtml
  92. +18 −0 tests/ServiceStack.ServiceHost.Tests/Views/Shared/DynamicShared.cshtml
  93. +18 −0 tests/ServiceStack.ServiceHost.Tests/Views/Shared/DynamicTplShared.cshtml
  94. +17 −0 tests/ServiceStack.ServiceHost.Tests/Views/Template/Customer.cshtml
  95. +33 −0 tests/ServiceStack.ServiceHost.Tests/Views/Template/CustomerDetailsResponse.cshtml
  96. +25 −0 tests/ServiceStack.ServiceHost.Tests/Views/Template/DynamicListTpl.cshtml
  97. +29 −0 tests/ServiceStack.ServiceHost.Tests/Views/Template/DynamicNestedTpl.cshtml
  98. +20 −0 tests/ServiceStack.ServiceHost.Tests/Views/Template/DynamicTpl.cshtml
  99. +20 −0 tests/ServiceStack.ServiceHost.Tests/Views/Template/default.cshtml
  100. +1 −1 tests/ServiceStack.WebHost.Endpoints.Tests/FileUploadTests.cs
  101. +2 −2 tests/ServiceStack.WebHost.Endpoints.Tests/IntegrationTests/IntegrationTestBase.cs
  102. +2 −0 tests/ServiceStack.WebHost.Endpoints.Tests/IocServiceTests.cs
  103. +1 −0 tests/ServiceStack.WebHost.Endpoints.Tests/ServiceStack.WebHost.Endpoints.Tests.csproj
  104. +9 −0 tests/ServiceStack.WebHost.Endpoints.Tests/Support/Host/IocAppHost.cs
  105. +1 −1 tests/ServiceStack.WebHost.Endpoints.Tests/Support/ServiceClientTestBase.cs
  106. +12 −4 tests/ServiceStack.WebHost.Endpoints.Tests/Support/Services/IocService.cs
  107. +62 −0 tests/ServiceStack.WebHost.Endpoints.Tests/_JsonpTests.cs
  108. +1 −0 tests/ServiceStack.WebHost.Endpoints.Tests/_SyncRestClientTests.cs
View
51 src/RazorEngine/Compilation/CSharp/CSharpDirectCompilerService.cs
@@ -0,0 +1,51 @@
+namespace RazorEngine.Compilation.CSharp
+{
+ using System;
+ using System.Linq;
+ using System.Web.Razor.Parser;
+
+ using Microsoft.CSharp;
+
+ /// <summary>
+ /// Defines a direct compiler service for the C# syntax.
+ /// </summary>
+ public class CSharpDirectCompilerService : DirectCompilerServiceBase
+ {
+
+ #region Constructor
+ /// <summary>
+ /// Initialises a new instance of <see cref="CSharpDirectCompilerService"/>.
+ /// </summary>
+ /// <param name="strictMode">Specifies whether the strict mode parsing is enabled.</param>
+ /// <param name="markupParser">The markup parser to use.</param>
+ public CSharpDirectCompilerService(bool strictMode = true, MarkupParser markupParser = null)
+ : base(
+ new CSharpRazorCodeLanguage(strictMode),
+ new CSharpCodeProvider(),
+ markupParser) { }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Builds a type name for the specified generic type.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="isDynamic">Specifies whether the type is dynamic.</param>
+ /// <returns>
+ /// The string typename (including namespace and generic type parameters).
+ /// </returns>
+ public override string BuildTypeNameInternal(Type type, bool isDynamic)
+ {
+ if (!type.IsGenericType)
+ return type.FullName;
+
+ return type.Namespace
+ + "."
+ + type.Name.Substring(0, type.Name.IndexOf('`'))
+ + "<"
+ + (isDynamic ? "dynamic" : string.Join(", ", type.GetGenericArguments().Select(t => BuildTypeNameInternal(t, CompilerServices.IsDynamicType(t)))))
+ + ">";
+ }
+ #endregion
+ }
+}
View
48 src/RazorEngine/Compilation/CSharp/CSharpRazorCodeGenerator.cs
@@ -0,0 +1,48 @@
+namespace RazorEngine.Compilation.CSharp
+{
+ using System.Web.Razor;
+ using System.Web.Razor.Parser.SyntaxTree;
+
+ using Templating;
+
+ /// <summary>
+ /// Defines a code generator that supports C# syntax.
+ /// </summary>
+ public class CSharpRazorCodeGenerator : System.Web.Razor.Generator.CSharpRazorCodeGenerator
+ {
+ #region Constructor
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CSharpRazorCodeGenerator"/> class.
+ /// </summary>
+ /// <param name="className">Name of the class.</param>
+ /// <param name="rootNamespaceName">Name of the root namespace.</param>
+ /// <param name="sourceFileName">Name of the source file.</param>
+ /// <param name="host">The host.</param>
+ /// <param name="strictMode">Flag to specify that this generator is running in struct mode.</param>
+ public CSharpRazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host, bool strictMode)
+ : base(className, rootNamespaceName, sourceFileName, host)
+ {
+ StrictMode = strictMode;
+ }
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets whether the code generator is running in strict mode.
+ /// </summary>
+ public bool StrictMode { get; private set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Visits an error generated through parsing.
+ /// </summary>
+ /// <param name="err">The error that was generated.</param>
+ public override void VisitError(RazorError err)
+ {
+ if (StrictMode)
+ throw new TemplateParsingException(err);
+ }
+ #endregion
+ }
+}
View
41 src/RazorEngine/Compilation/CSharp/CSharpRazorCodeLanguage.cs
@@ -0,0 +1,41 @@
+namespace RazorEngine.Compilation.CSharp
+{
+ using System.Web.Razor;
+ using System.Web.Razor.Generator;
+
+ public class CSharpRazorCodeLanguage : System.Web.Razor.CSharpRazorCodeLanguage
+ {
+ #region Constructor
+ /// <summary>
+ /// Initialises a new instance
+ /// </summary>
+ /// <param name="strictMode">Flag to determine whether strict mode is enabled.</param>
+ public CSharpRazorCodeLanguage(bool strictMode)
+ {
+ StrictMode = strictMode;
+ }
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets whether strict mode is enabled.
+ /// </summary>
+ public bool StrictMode { get; private set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Creates the code generator.
+ /// </summary>
+ /// <param name="className">Name of the class.</param>
+ /// <param name="rootNamespaceName">Name of the root namespace.</param>
+ /// <param name="sourceFileName">Name of the source file.</param>
+ /// <param name="host">The host.</param>
+ /// <returns>An instance of <see cref="RazorCodeGenerator"/>.</returns>
+ public override RazorCodeGenerator CreateCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host)
+ {
+ return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host, StrictMode);
+ }
+ #endregion
+ }
+}
View
191 src/RazorEngine/Compilation/CompilerServiceBase.cs
@@ -0,0 +1,191 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+ using System.CodeDom;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Razor;
+ using System.Web.Razor.Generator;
+ using System.Web.Razor.Parser;
+
+ using Templating;
+
+ /// <summary>
+ /// Provides a base implementation of a compiler service.
+ /// </summary>
+ public abstract class CompilerServiceBase : ICompilerService
+ {
+ #region Constructor
+ /// <summary>
+ /// Initialises a new instance of <see cref="CompilerServiceBase"/>
+ /// </summary>
+ /// <param name="codeLanguage">The code language.</param>
+ /// <param name="markupParser">The markup parser.</param>
+ protected CompilerServiceBase(RazorCodeLanguage codeLanguage, MarkupParser markupParser)
+ {
+ if (codeLanguage == null)
+ throw new ArgumentNullException("codeLanguage");
+
+ CodeLanguage = codeLanguage;
+ MarkupParser = markupParser ?? new HtmlMarkupParser();
+ }
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets the code language.
+ /// </summary>
+ public RazorCodeLanguage CodeLanguage { get; private set; }
+
+ /// <summary>
+ /// Gets the markup parser.
+ /// </summary>
+ public MarkupParser MarkupParser { get; private set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Builds a type name for the specified template type and model type.
+ /// </summary>
+ /// <param name="templateType">The template type.</param>
+ /// <param name="modelType">The model type.</param>
+ /// <returns>The string type name (including namespace).</returns>
+ public virtual string BuildTypeName(Type templateType, Type modelType)
+ {
+ if (templateType == null)
+ throw new ArgumentNullException("templateType");
+
+ if (!templateType.IsGenericTypeDefinition && !templateType.IsGenericType)
+ return templateType.FullName;
+
+ if (modelType == null)
+ throw new ArgumentException("The template type is a generic defintion, and no model type has been supplied.");
+
+ bool @dynamic = CompilerServices.IsDynamicType(modelType);
+ Type genericType = templateType.MakeGenericType(modelType);
+
+ return BuildTypeNameInternal(genericType, @dynamic);
+ }
+
+ /// <summary>
+ /// Builds a type name for the specified generic type.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="isDynamic">Is the model type dynamic?</param>
+ /// <returns>The string typename (including namespace and generic type parameters).</returns>
+ public abstract string BuildTypeNameInternal(Type type, bool isDynamic);
+
+ /// <summary>
+ /// Compiles the type defined in the specified type context.
+ /// </summary>
+ /// <param name="context">The type context which defines the type to compile.</param>
+ /// <returns>The compiled type.</returns>
+ public abstract Type CompileType(TypeContext context);
+
+ /// <summary>
+ /// Generates any required contructors for the specified type.
+ /// </summary>
+ /// <param name="constructors">The set of constructors.</param>
+ /// <param name="codeType">The code type declaration.</param>
+ private static void GenerateConstructors(IEnumerable<ConstructorInfo> constructors, CodeTypeDeclaration codeType)
+ {
+ if (constructors == null || !constructors.Any())
+ return;
+
+ var existingConstructors = codeType.Members.OfType<CodeConstructor>().ToArray();
+ foreach (var existingConstructor in existingConstructors)
+ codeType.Members.Remove(existingConstructor);
+
+ foreach (var constructor in constructors)
+ {
+ var ctor = new CodeConstructor();
+ ctor.Attributes = MemberAttributes.Public;
+
+ foreach (var param in constructor.GetParameters())
+ {
+ ctor.Parameters.Add(new CodeParameterDeclarationExpression(param.ParameterType, param.Name));
+ ctor.BaseConstructorArgs.Add(new CodeSnippetExpression(param.Name));
+ }
+
+ codeType.Members.Add(ctor);
+ }
+ }
+
+ /// <summary>
+ /// Gets the code compile unit used to compile a type.
+ /// </summary>
+ /// <param name="className">The class name.</param>
+ /// <param name="template">The template to compile.</param>
+ /// <param name="namespaceImports">The set of namespace imports.</param>
+ /// <param name="templateType">The template type.</param>
+ /// <param name="modelType">The model type.</param>
+ /// <returns>A <see cref="CodeCompileUnit"/> used to compile a type.</returns>
+ public CodeCompileUnit GetCodeCompileUnit(string className, string template, ISet<string> namespaceImports, Type templateType, Type modelType)
+ {
+ if (string.IsNullOrEmpty(className))
+ throw new ArgumentException("Class name is required.");
+
+ if (string.IsNullOrEmpty(template))
+ throw new ArgumentException("Template is required.");
+
+ templateType = templateType
+ ?? ((modelType == null)
+ ? typeof(TemplateBase)
+ : typeof(TemplateBase<>));
+
+ var host = new RazorEngineHost(CodeLanguage, () => MarkupParser)
+ {
+ DefaultBaseClass = BuildTypeName(templateType, modelType),
+ DefaultClassName = className,
+ DefaultNamespace = "CompiledRazorTemplates.Dynamic",
+ GeneratedClassContext = new GeneratedClassContext("Execute", "Write", "WriteLiteral",
+ "WriteTo", "WriteLiteralTo",
+ "RazorEngine.Templating.TemplateWriter",
+ "WriteSection")
+ };
+
+ var templateNamespaces = templateType.GetCustomAttributes(typeof (RequireNamespacesAttribute), true)
+ .Cast<RequireNamespacesAttribute>()
+ .SelectMany(att => att.Namespaces);
+
+ foreach (string ns in templateNamespaces)
+ namespaceImports.Add(ns);
+
+ foreach (string @namespace in namespaceImports)
+ host.NamespaceImports.Add(@namespace);
+
+ var engine = new RazorTemplateEngine(host);
+ GeneratorResults result;
+ using (var reader = new StringReader(template))
+ {
+ result = engine.GenerateCode(reader);
+ }
+
+ var type = result.GeneratedCode.Namespaces[0].Types[0];
+ if (modelType != null)
+ {
+ if (CompilerServices.IsAnonymousType(modelType))
+ {
+ type.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(HasDynamicModelAttribute))));
+ }
+ }
+
+ GenerateConstructors(CompilerServices.GetConstructors(templateType), type);
+
+ var statement = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Clear");
+ foreach (CodeTypeMember member in type.Members)
+ {
+ if (member.Name.Equals("Execute"))
+ {
+ ((CodeMemberMethod)member).Statements.Insert(0, new CodeExpressionStatement(statement));
+ break;
+ }
+ }
+
+ return result.GeneratedCode;
+ }
+ #endregion
+ }
+}
View
90 src/RazorEngine/Compilation/CompilerServices.cs
@@ -0,0 +1,90 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Dynamic;
+ using System.Reflection;
+ using System.Runtime.CompilerServices;
+ using System.Text.RegularExpressions;
+
+ /// <summary>
+ /// Provides service methods for compilation.
+ /// </summary>
+ public static class CompilerServices
+ {
+ #region Fields
+ private static readonly Type DynamicType = typeof(DynamicObject);
+ private static readonly Type ExpandoType = typeof (ExpandoObject);
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Determines if the specified type is an anonymous type.
+ /// </summary>
+ /// <param name="type">The type to check.</param>
+ /// <returns>True if the type is an anonymous type, otherwise false.</returns>
+ public static bool IsAnonymousType(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ return (type.IsClass
+ && type.IsSealed
+ && type.BaseType == typeof(object)
+ && type.Name.StartsWith("<>")
+ && type.IsDefined(typeof(CompilerGeneratedAttribute), true));
+ }
+
+ /// <summary>
+ /// Determines if the specified type is a dynamic type.
+ /// </summary>
+ /// <param name="type">The type to check.</param>
+ /// <returns>True if the type is an anonymous type, otherwise false.</returns>
+ public static bool IsDynamicType(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ return (DynamicType.IsAssignableFrom(type)
+ || ExpandoType.IsAssignableFrom(type)
+ || IsAnonymousType(type));
+ }
+
+ /// <summary>
+ /// Generates a random class name.
+ /// </summary>
+ /// <returns>A new random class name.</returns>
+ public static string GenerateClassName()
+ {
+ Guid guid = Guid.NewGuid();
+ return Regex.Replace(guid.ToString("N"), @"[^A-Za-z]*", "");
+ }
+
+ /// <summary>
+ /// Gets the public or protected constructors of the specified type.
+ /// </summary>
+ /// <param name="type">The target type.</param>
+ /// <returns>An enumerable of constructors.</returns>
+ public static IEnumerable<ConstructorInfo> GetConstructors(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ var constructors = type
+ .GetConstructors(BindingFlags.Public | BindingFlags.Instance);
+
+ return constructors;
+ }
+
+ /// <summary>
+ /// Gets an enumerable of all assemblies loaded in the current domain.
+ /// </summary>
+ /// <returns>An enumerable of loaded assemblies.</returns>
+ public static IEnumerable<Assembly> GetLoadedAssemblies()
+ {
+ var domain = AppDomain.CurrentDomain;
+ return domain.GetAssemblies();
+ }
+ #endregion
+ }
+}
View
33 src/RazorEngine/Compilation/DefaultCompilerServiceFactory.cs
@@ -0,0 +1,33 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+ using System.Web.Razor.Parser;
+
+ using CSharp;
+
+ /// <summary>
+ /// Provides a default implementation of a compiler service factory.
+ /// </summary>
+ public class DefaultCompilerServiceFactory : ICompilerServiceFactory
+ {
+ #region Methods
+ /// <summary>
+ /// Creates an instance of a compiler service.
+ /// </summary>
+ /// <param name="language">The language to support in templates.</param>
+ /// <param name="strictMode">Strict mode forces parsing exceptions to be thrown.</param>
+ /// <param name="markupParser">The markup parser to use.</param>
+ /// <returns>An instance of <see cref="ICompilerService"/>.</returns>
+ public ICompilerService CreateCompilerService(Language language = Language.CSharp, bool strictMode = false, MarkupParser markupParser = null)
+ {
+ switch (language)
+ {
+ case Language.CSharp:
+ return new CSharpDirectCompilerService(strictMode, markupParser);
+ }
+
+ throw new ArgumentException("The language '" + language + "' is not supported.");
+ }
+ #endregion
+ }
+}
View
89 src/RazorEngine/Compilation/DirectCompilerServiceBase.cs
@@ -0,0 +1,89 @@
+using ServiceStack.Text;
+
+namespace RazorEngine.Compilation
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.Linq;
+ using System.Web.Razor;
+ using System.Web.Razor.Parser;
+
+ using Templating;
+
+ /// <summary>
+ /// Provides a base implementation of a direct compiler service.
+ /// </summary>
+ public abstract class DirectCompilerServiceBase : CompilerServiceBase
+ {
+ #region Fields
+ private readonly CodeDomProvider CodeDomProvider;
+ #endregion
+
+ #region Constructor
+ /// <summary>
+ /// Initialises a new instance of <see cref="DirectCompilerServiceBase"/>.
+ /// </summary>
+ /// <param name="codeLanguage">The razor code language.</param>
+ /// <param name="codeDomProvider">The code dom provider used to generate code.</param>
+ /// <param name="markupParser">The markup parser.</param>
+ protected DirectCompilerServiceBase(RazorCodeLanguage codeLanguage, CodeDomProvider codeDomProvider, MarkupParser markupParser)
+ : base(codeLanguage, markupParser)
+ {
+
+ if (codeDomProvider == null)
+ throw new ArgumentNullException("codeDomProvider");
+
+ CodeDomProvider = codeDomProvider;
+ }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Creates the compile results for the specified <see cref="TypeContext"/>.
+ /// </summary>
+ /// <param name="context">The type context.</param>
+ /// <returns>The compiler results.</returns>
+ private CompilerResults Compile(TypeContext context)
+ {
+ var compileUnit = GetCodeCompileUnit(context.ClassName, context.TemplateContent, context.Namespaces,
+ context.TemplateType, context.ModelType);
+
+ var @params = new CompilerParameters
+ {
+ GenerateInMemory = true,
+ GenerateExecutable = false,
+ IncludeDebugInformation = false,
+ CompilerOptions = "/target:library /optimize"
+ };
+
+ var assemblies = CompilerServices
+ .GetLoadedAssemblies()
+ .Where(a => !a.IsDynamic)
+ .Select(a => a.Location)
+ .ToArray();
+
+ @params.ReferencedAssemblies.AddRange(assemblies);
+
+ return CodeDomProvider.CompileAssemblyFromDom(@params, compileUnit);
+ }
+
+ /// <summary>
+ /// Compiles the type defined in the specified type context.
+ /// </summary>
+ /// <param name="context">The type context which defines the type to compile.</param>
+ /// <returns>The compiled type.</returns>
+ public override Type CompileType(TypeContext context)
+ {
+ var results = Compile(context);
+
+ if (results.Errors != null && results.Errors.Count > 0)
+ {
+ Console.WriteLine(results.Errors.Dump());
+ throw new TemplateCompilationException(results.Errors);
+ }
+
+ return results.CompiledAssembly.GetType("CompiledRazorTemplates.Dynamic." + context.ClassName);
+ }
+ #endregion
+ }
+}
View
12 src/RazorEngine/Compilation/HasDynamicModelAttribute.cs
@@ -0,0 +1,12 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+
+ /// <summary>
+ /// Defines an attribute that marks the presence of a dynamic model in a template.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+ public sealed class HasDynamicModelAttribute : Attribute
+ {
+ }
+}
View
27 src/RazorEngine/Compilation/ICompilerService.cs
@@ -0,0 +1,27 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+
+ /// <summary>
+ /// Defines the required contract for implementing a compiler service.
+ /// </summary>
+ public interface ICompilerService
+ {
+ #region Methods
+ /// <summary>
+ /// Builds a type name for the specified template type and model type.
+ /// </summary>
+ /// <param name="templateType">The template type.</param>
+ /// <param name="modelType">The model type.</param>
+ /// <returns>The string type name (including namespace).</returns>
+ string BuildTypeName(Type templateType, Type modelType);
+
+ /// <summary>
+ /// Compiles the type defined in the specified type context.
+ /// </summary>
+ /// <param name="context">The type context which defines the type to compile.</param>
+ /// <returns>The compiled type.</returns>
+ Type CompileType(TypeContext context);
+ #endregion
+ }
+}
View
21 src/RazorEngine/Compilation/ICompilerServiceFactory.cs
@@ -0,0 +1,21 @@
+namespace RazorEngine.Compilation
+{
+ using System.Web.Razor.Parser;
+
+ /// <summary>
+ /// Defines the required contract for implementing a compiler service factory.
+ /// </summary>
+ public interface ICompilerServiceFactory
+ {
+ #region Methods
+ /// <summary>
+ /// Creates an instance of a compiler service.
+ /// </summary>
+ /// <param name="language">The language to support in templates.</param>
+ /// <param name="strictMode">Strict mode forces parsing exceptions to be thrown.</param>
+ /// <param name="markupParser">The markup parser to use.</param>
+ /// <returns>An instance of <see cref="ICompilerService"/>.</returns>
+ ICompilerService CreateCompilerService(Language language = Language.CSharp, bool strictMode = false, MarkupParser markupParser = null);
+ #endregion
+ }
+}
View
56 src/RazorEngine/Compilation/RazorDynamicObject.cs
@@ -0,0 +1,56 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+ using System.Diagnostics;
+ using System.Dynamic;
+
+ /// <summary>
+ /// Defines a dynamic object.
+ /// </summary>
+ internal class RazorDynamicObject : DynamicObject
+ {
+ #region Properties
+ /// <summary>
+ /// Gets or sets the model.
+ /// </summary>
+ public object Model { get; set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Gets the value of the specified member.
+ /// </summary>
+ /// <param name="binder">The current binder.</param>
+ /// <param name="result">The member result.</param>
+ /// <returns>True.</returns>
+ [DebuggerStepThrough]
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ var dynamicObject = Model as RazorDynamicObject;
+ if (dynamicObject != null)
+ return dynamicObject.TryGetMember(binder, out result);
+
+ Type modelType = Model.GetType();
+ var prop = modelType.GetProperty(binder.Name);
+ if (prop == null)
+ {
+ result = null;
+ return false;
+ }
+
+ object value = prop.GetValue(Model, null);
+ if (value == null)
+ {
+ result = value;
+ return true;
+ }
+
+ Type valueType = value.GetType();
+ result = (CompilerServices.IsAnonymousType(valueType))
+ ? new RazorDynamicObject { Model = value }
+ : value;
+ return true;
+ }
+ #endregion
+ }
+}
View
49 src/RazorEngine/Compilation/TypeContext.cs
@@ -0,0 +1,49 @@
+namespace RazorEngine.Compilation
+{
+ using System;
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// Defines a type context used for compilation.
+ /// </summary>
+ public class TypeContext
+ {
+ #region Constructor
+ /// <summary>
+ /// Initialises a new instance of <see cref="TypeContext"/>.
+ /// </summary>
+ public TypeContext()
+ {
+ ClassName = CompilerServices.GenerateClassName();
+ Namespaces = new HashSet<string>();
+ }
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets or sets the class name.
+ /// </summary>
+ public string ClassName { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the model type.
+ /// </summary>
+ public Type ModelType { get; set; }
+
+ /// <summary>
+ /// Gets the set of namespace imports.
+ /// </summary>
+ public ISet<string> Namespaces { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the template content.
+ /// </summary>
+ public string TemplateContent { get; set; }
+
+ /// <summary>
+ /// Gets or sets the template type.
+ /// </summary>
+ public Type TemplateType { get; set; }
+ #endregion
+ }
+}
View
68 src/RazorEngine/Configuration/ConfigurationServices.cs
@@ -0,0 +1,68 @@
+namespace RazorEngine.Configuration
+{
+ using System;
+
+ using Templating;
+
+ /// <summary>
+ /// Provides configuration service operations.
+ /// </summary>
+ public static class ConfigurationServices
+ {
+ #region Methods
+ /// <summary>
+ /// Adds any configured namespaces to the target template service.
+ /// </summary>
+ /// <param name="service">The template service.</param>
+ /// <param name="config">The namespace configuration element.</param>
+ public static void AddNamespaces(TemplateService service, NamespaceConfigurationElementCollection config)
+ {
+ foreach (NamespaceConfigurationElement @namespace in config)
+ {
+ service.Namespaces.Add(@namespace.Namespace);
+ }
+ }
+
+ /// <summary>
+ /// Creates an instance of the specified type.
+ /// </summary>
+ /// <typeparam name="T">The expected type.</typeparam>
+ /// <param name="typeName">The type name.</param>
+ /// <returns>An instance of the specified type.</returns>
+ public static T CreateInstance<T>(string typeName) where T : class
+ {
+ if (string.IsNullOrWhiteSpace(typeName))
+ throw new ArgumentException("Type name is required.");
+
+ Type type = Type.GetType(typeName);
+ if (type == null)
+ throw new ArgumentException("The type '" + typeName + "' could not be loaded.");
+
+ T instance = Activator.CreateInstance(type) as T;
+ if (instance == null)
+ throw new ArgumentException("The type '" + typeName + "' is not an instance of '" + typeof(T).FullName + "'.");
+
+ return instance;
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="TemplateService"/> from the specified configuration.
+ /// </summary>
+ /// <param name="config">The template service configuration.</param>
+ /// <returns>An instance of <see cref="TemplateService"/>.</returns>
+ public static TemplateService CreateTemplateService(TemplateServiceConfigurationElement config)
+ {
+ if (config == null)
+ throw new ArgumentNullException("config");
+
+ var service = TemplateServiceFactory.CreateTemplateService(config);
+ AddNamespaces(service, config.Namespaces);
+
+ if (!string.IsNullOrWhiteSpace(config.Activator))
+ service.SetActivator(CreateInstance<IActivator>(config.Activator));
+
+ return service;
+ }
+ #endregion
+ }
+}
View
26 src/RazorEngine/Configuration/NamespaceConfigurationElement.cs
@@ -0,0 +1,26 @@
+namespace RazorEngine.Configuration
+{
+ using System.Configuration;
+
+ /// <summary>
+ /// Defines the <see cref="ConfigurationElement"/> that represents a namespaces element.
+ /// </summary>
+ public class NamespaceConfigurationElement : ConfigurationElement
+ {
+ #region Fields
+ private const string NamespaceAttribute = "namespace";
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets or sets the namespace.
+ /// </summary>
+ [ConfigurationProperty(NamespaceAttribute, IsRequired = true, IsKey = true)]
+ public string Namespace
+ {
+ get { return (string)this[NamespaceAttribute]; }
+ set { this[NamespaceAttribute] = value; }
+ }
+ #endregion
+ }
+}
View
36 src/RazorEngine/Configuration/NamespaceConfigurationElementCollection.cs
@@ -0,0 +1,36 @@
+namespace RazorEngine.Configuration
+{
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents a collection of <see cref="NamespaceConfigurationElement"/> items.
+ /// </summary>
+ [ConfigurationCollection(typeof(NamespaceConfigurationElement))]
+ public class NamespaceConfigurationElementCollection : ConfigurationElementCollection
+ {
+ #region Methods
+ /// <summary>
+ /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
+ /// </summary>
+ /// <returns>
+ /// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
+ /// </returns>
+ protected override ConfigurationElement CreateNewElement()
+ {
+ return new NamespaceConfigurationElement();
+ }
+
+ /// <summary>
+ /// Gets the element key for a specified configuration element when overridden in a derived class.
+ /// </summary>
+ /// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.</param>
+ /// <returns>
+ /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
+ /// </returns>
+ protected override object GetElementKey(ConfigurationElement element)
+ {
+ return ((NamespaceConfigurationElement)element).Namespace;
+ }
+ #endregion
+ }
+}
View
71 src/RazorEngine/Configuration/RazorEngineConfigurationSection.cs
@@ -0,0 +1,71 @@
+namespace RazorEngine.Configuration
+{
+ using System.Configuration;
+
+ /// <summary>
+ /// Defines the main configuration section for the RazorEngine.
+ /// </summary>
+ public class RazorEngineConfigurationSection : ConfigurationSection
+ {
+ #region Fields
+ private const string ActivatorAttribute = "activator";
+ private const string FactoryAttribute = "factory";
+ private const string NamespacesElement = "namespaces";
+ private const string SectionPath = "razorEngine";
+ private const string TemplateServicesElement = "templateServices";
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets the activator used for this template service.
+ /// </summary>
+ [ConfigurationProperty(ActivatorAttribute, IsRequired = false)]
+ public string Activator
+ {
+ get { return (string)this[ActivatorAttribute]; }
+ set { this[ActivatorAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the factory used to create compiler service instances.
+ /// </summary>
+ [ConfigurationProperty(FactoryAttribute)]
+ public string Factory
+ {
+ get { return (string)this[FactoryAttribute]; }
+ set { this[FactoryAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the collection of namespaces.
+ /// </summary>
+ [ConfigurationProperty(NamespacesElement)]
+ public NamespaceConfigurationElementCollection Namespaces
+ {
+ get { return (NamespaceConfigurationElementCollection)this[NamespacesElement]; }
+ set { this[NamespacesElement] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the collection of template service configurations.
+ /// </summary>
+ [ConfigurationProperty(TemplateServicesElement)]
+ public TemplateServiceConfigurationElementConfiguration TemplateServices
+ {
+ get { return (TemplateServiceConfigurationElementConfiguration)this[TemplateServicesElement]; }
+ set { this[TemplateServicesElement] = value; }
+ }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Gets an instance of <see cref="RazorEngineConfigurationSection"/> that represents the current configuration.
+ /// </summary>
+ /// <returns>An instance of <see cref="RazorEngineConfigurationSection"/>, or null if no configuration is specified.</returns>
+ public static RazorEngineConfigurationSection GetConfiguration()
+ {
+ return ConfigurationManager.GetSection(SectionPath) as RazorEngineConfigurationSection;
+ }
+ #endregion
+ }
+}
View
92 src/RazorEngine/Configuration/TemplateServiceConfigurationElement.cs
@@ -0,0 +1,92 @@
+namespace RazorEngine.Configuration
+{
+ using System.Configuration;
+
+ /// <summary>
+ /// Defines the <see cref="ConfigurationElement"/> that represents a template service element.
+ /// </summary>
+ public class TemplateServiceConfigurationElement : ConfigurationElement
+ {
+ #region Fields
+ private const string ActivatorAttribute = "activator";
+ private const string LanguageAttribute = "language";
+ private const string MarkupParserAttribute = "markupParser";
+ private const string NameAttribute = "name";
+ private const string NamespacesAttribute = "namespaces";
+ private const string StrictModeAttribute = "strictMode";
+ private const string TemplateBaseAttribute = "templateBase";
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets the activator used for this template service.
+ /// </summary>
+ [ConfigurationProperty(ActivatorAttribute, IsRequired = false)]
+ public string Activator
+ {
+ get { return (string)this[ActivatorAttribute]; }
+ set { this[ActivatorAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Defines the language supported by the template service.
+ /// </summary>
+ [ConfigurationProperty(LanguageAttribute, IsRequired = false, DefaultValue = Language.CSharp)]
+ public Language Language
+ {
+ get { return (Language)this[LanguageAttribute]; }
+ set { this[LanguageAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the markup parser.
+ /// </summary>
+ [ConfigurationProperty(MarkupParserAttribute)]
+ public string MarkupParser
+ {
+ get { return (string)this[MarkupParserAttribute]; }
+ set { this[MarkupParserAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the name of the template service.
+ /// </summary>
+ [ConfigurationProperty(NameAttribute, IsRequired = true, IsKey = true)]
+ public string Name
+ {
+ get { return (string)this[NameAttribute]; }
+ set { this[NameAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the collection of namespaces.
+ /// </summary>
+ [ConfigurationProperty(NamespacesAttribute)]
+ public NamespaceConfigurationElementCollection Namespaces
+ {
+ get { return (NamespaceConfigurationElementCollection)this[NamespacesAttribute]; }
+ set { this[NamespacesAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets whether the template service should be running in strict mode.
+ /// </summary>
+ [ConfigurationProperty(StrictModeAttribute)]
+ public bool StrictMode
+ {
+ get { return (bool)this[StrictModeAttribute]; }
+ set { this[StrictModeAttribute] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the template base
+ /// </summary>
+ [ConfigurationProperty(TemplateBaseAttribute)]
+ public string TemplateBase
+ {
+ get { return (string)this[TemplateBaseAttribute]; }
+ set { this[TemplateBaseAttribute] = value; }
+ }
+ #endregion
+ }
+}
View
66 src/RazorEngine/Configuration/TemplateServiceConfigurationElementCollection.cs
@@ -0,0 +1,66 @@
+namespace RazorEngine.Configuration
+{
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents a collection of <see cref="TemplateServiceConfigurationElement"/> items.
+ /// </summary>
+ [ConfigurationCollection(typeof(TemplateServiceConfigurationElement))]
+ public class TemplateServiceConfigurationElementConfiguration : ConfigurationElementCollection
+ {
+ #region Fields
+ private const string DefaultAttribute = "default";
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets or sets the default template service.
+ /// </summary>
+ public string Default { get; private set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
+ /// </summary>
+ /// <returns>
+ /// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
+ /// </returns>
+ protected override ConfigurationElement CreateNewElement()
+ {
+ return new TemplateServiceConfigurationElement();
+ }
+
+ /// <summary>
+ /// Gets the element key for a specified configuration element when overridden in a derived class.
+ /// </summary>
+ /// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.</param>
+ /// <returns>
+ /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
+ /// </returns>
+ protected override object GetElementKey(ConfigurationElement element)
+ {
+ return ((TemplateServiceConfigurationElement)element).Name;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether an unknown attribute is encountered during deserialization.
+ /// </summary>
+ /// <param name="name">The name of the unrecognized attribute.</param>
+ /// <param name="value">The value of the unrecognized attribute.</param>
+ /// <returns>
+ /// true when an unknown attribute is encountered while deserializing; otherwise, false.
+ /// </returns>
+ protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
+ {
+ if (name.Equals("default"))
+ {
+ Default = value;
+ return true;
+ }
+
+ return base.OnDeserializeUnrecognizedAttribute(name, value);
+ }
+ #endregion
+ }
+}
View
14 src/RazorEngine/IRazorTemplate.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using RazorEngine.Templating;
+using ServiceStack.Markdown;
+
+namespace RazorEngine
+{
+ public interface IRazorTemplate : ITemplate, ITemplatePage
+ {
+ string Layout { get; }
+ Dictionary<string, Action> Sections { get; }
+ IRazorTemplate ChildTemplate { get; set; }
+ }
+}
View
13 src/RazorEngine/Language.cs
@@ -0,0 +1,13 @@
+namespace RazorEngine
+{
+ /// <summary>
+ /// Defines the supported languages.
+ /// </summary>
+ public enum Language
+ {
+ /// <summary>
+ /// C# Language
+ /// </summary>
+ CSharp,
+ }
+}
View
10 src/RazorEngine/MicrosoftCSharpReferenceStub.cs
@@ -0,0 +1,10 @@
+namespace RazorEngine
+{
+ using Microsoft.CSharp.RuntimeBinder;
+
+ /// <summary>
+ /// Defines an internal stub type that enforces that the Microsoft.CSharp assembly is
+ /// referenced in the dynamically compiled template assemblies.
+ /// </summary>
+ internal class MicrosoftCSharpReferenceStub : RuntimeBinderException { }
+}
View
365 src/RazorEngine/MvcRazorFormat.cs
@@ -0,0 +1,365 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.IO;
+using System.Linq;
+using RazorEngine.Templating;
+using ServiceStack.Common;
+using ServiceStack.Common.Utils;
+using ServiceStack.Logging;
+using ServiceStack.Markdown;
+using ServiceStack.ServiceHost;
+using ServiceStack.Text;
+using ServiceStack.WebHost.Endpoints;
+
+namespace RazorEngine
+{
+ public enum RazorPageType
+ {
+ ContentPage = 1,
+ ViewPage = 2,
+ SharedViewPage = 3,
+ Template = 4,
+ }
+
+ public class MvcRazorFormat : ITemplateResolver, IActivator
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(MvcRazorFormat));
+
+ private const string ErrorPageNotFound = "Could not find Razor page '{0}'";
+
+ public static string TemplateName = "default.cshtml";
+ public static string TemplatePlaceHolder = "@RenderBody()";
+
+ // ~/View - Dynamic Pages
+ public Dictionary<string, RazorPage> ViewPages = new Dictionary<string, RazorPage>(
+ StringComparer.CurrentCultureIgnoreCase);
+
+ // ~/View/Shared - Dynamic Shared Pages
+ public Dictionary<string, RazorPage> ViewSharedPages = new Dictionary<string, RazorPage>(
+ StringComparer.CurrentCultureIgnoreCase);
+
+ //Content Pages outside of ~/View
+ public Dictionary<string, RazorPage> ContentPages = new Dictionary<string, RazorPage>(
+ StringComparer.CurrentCultureIgnoreCase);
+
+ public Dictionary<string, RazorPage> PageTemplates = new Dictionary<string, RazorPage>(
+ StringComparer.CurrentCultureIgnoreCase);
+
+ public IAppHost AppHost { get; set; }
+
+ public Dictionary<string, string> MarkdownReplaceTokens { get; set; }
+
+ public Func<string, IEnumerable<RazorPage>> FindRazorPagesFn { get; set; }
+
+ public MvcRazorFormat()
+ {
+ this.FindRazorPagesFn = FindRazorPages;
+ this.MarkdownReplaceTokens = new Dictionary<string, string>();
+ }
+
+ public void Register(IAppHost appHost)
+ {
+ this.AppHost = appHost;
+ this.MarkdownReplaceTokens = new Dictionary<string, string>(appHost.Config.MarkdownReplaceTokens);
+ if (!appHost.Config.WebHostUrl.IsNullOrEmpty())
+ this.MarkdownReplaceTokens["~/"] = appHost.Config.WebHostUrl.WithTrailingSlash();
+
+ var razorBaseType = appHost.Config.RazorBaseType;
+ Init(razorBaseType);
+
+ RegisterRazorPages(appHost.Config.MarkdownSearchPath);
+
+ //Render HTML
+ appHost.HtmlProviders.Add((requestContext, dto, httpRes) => {
+
+ var httpReq = requestContext.Get<IHttpRequest>();
+ RazorPage razorPage;
+ if ((razorPage = GetViewPageByResponse(dto, httpReq)) == null)
+ return false;
+
+ ReloadModifiedPageAndTemplates(razorPage);
+
+ return ProcessRazorPage(httpReq, razorPage, dto, httpRes);
+ });
+
+ appHost.CatchAllHandlers.Add((httpMethod, pathInfo, filePath) => {
+ RazorPage razorPage;
+ if (filePath == null || (razorPage = GetContentPage(filePath.WithoutExtension())) == null) return null;
+ return new RazorHandler {
+ MvcRazorFormat = this,
+ RazorPage = razorPage,
+ RequestName = "RazorPage",
+ PathInfo = pathInfo,
+ FilePath = filePath
+ };
+ });
+ }
+
+ public void Init(Type razorBaseType = null)
+ {
+ var loaded = typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly != null;
+ if (!loaded)
+ throw new ConfigurationErrorsException("Microsoft.CSharp not properly loaded");
+
+ if (razorBaseType != null)
+ {
+ if (!razorBaseType.HasInterface(typeof(ITemplatePage)))
+ throw new ConfigurationErrorsException(razorBaseType.FullName + " must inherit from RazorBasePage");
+
+ Razor.SetTemplateBase(razorBaseType);
+ }
+ else
+ {
+ Razor.SetTemplateBase(typeof(RazorPageBase<>));
+ }
+
+ Razor.AddResolver(this);
+ Razor.SetActivator(this);
+ }
+
+ public IEnumerable<RazorPage> FindRazorPages(string dirPath)
+ {
+ var di = new DirectoryInfo(dirPath);
+ var razorFiles = di.GetMatchingFiles("*.cshtml");
+
+ var viewPath = Path.Combine(di.FullName, "Views");
+ var viewSharedPath = Path.Combine(viewPath, "Shared");
+
+ foreach (var razorFile in razorFiles)
+ {
+ var fileInfo = new FileInfo(razorFile);
+ var pageName = fileInfo.Name.WithoutExtension();
+ var pageContents = File.ReadAllText(razorFile);
+
+ var pageType = RazorPageType.ContentPage;
+ if (fileInfo.FullName.StartsWithIgnoreCase(viewSharedPath))
+ pageType = RazorPageType.SharedViewPage;
+ else if (fileInfo.FullName.StartsWithIgnoreCase(viewPath))
+ pageType = RazorPageType.ViewPage;
+
+ var templatePath = GetTemplatePath(fileInfo.DirectoryName);
+
+ yield return new RazorPage(this, razorFile, pageName, pageContents, pageType) {
+ TemplatePath = templatePath,
+ LastModified = fileInfo.LastWriteTime,
+ };
+ }
+ }
+
+ readonly Dictionary<string,string> templatePathsFound = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+ readonly HashSet<string> templatePathsNotFound = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+
+ private string GetTemplatePath(string fileDirPath)
+ {
+ if (templatePathsNotFound.Contains(fileDirPath)) return null;
+
+ var templateDirPath = fileDirPath;
+ string templatePath;
+ while (templateDirPath != null && !File.Exists(Path.Combine(templateDirPath, TemplateName)))
+ {
+ if (templatePathsFound.TryGetValue(templateDirPath, out templatePath))
+ return templatePath;
+
+ templateDirPath = templateDirPath.ParentDirectory();
+ }
+
+ if (templateDirPath != null)
+ {
+ templatePath = Path.Combine(templateDirPath, TemplateName);
+ templatePathsFound[templateDirPath] = templatePath;
+ return templatePath;
+ }
+
+ templatePathsNotFound.Add(fileDirPath);
+ return null;
+ }
+
+ public bool ProcessRazorPage(IHttpRequest httpReq, RazorPage razorPage, object dto, IHttpResponse httpRes)
+ {
+ httpRes.AddHeaderLastModified(razorPage.GetLastModified());
+
+ var renderInTemplate = true;
+ if (httpReq != null && httpReq.QueryString["format"] != null)
+ {
+ renderInTemplate = !httpReq.GetFormatModifier().StartsWithIgnoreCase("bare");
+ }
+
+ var markup = RenderDynamicPage(razorPage, razorPage.Name, dto, renderInTemplate);
+ var markupBytes = markup.ToUtf8Bytes();
+ httpRes.OutputStream.Write(markupBytes, 0, markupBytes.Length);
+
+ return true;
+ }
+
+ private string RenderDynamicPage(RazorPage razorPage, string pageName, object model, bool renderTemplate)
+ {
+ if (razorPage == null)
+ throw new InvalidDataException(ErrorPageNotFound.FormatWith(pageName));
+
+ return Razor.Run(model, razorPage.PageName);
+ }
+
+ public void ReloadModifiedPageAndTemplates(RazorPage razorPage)
+ {
+ var lastWriteTime = File.GetLastWriteTime(razorPage.FilePath);
+ if (lastWriteTime > razorPage.LastModified)
+ {
+ razorPage.Reload();
+ }
+
+ RazorPage template;
+ if (razorPage.DirectiveTemplatePath != null
+ && this.PageTemplates.TryGetValue(razorPage.DirectiveTemplatePath, out template))
+ {
+ lastWriteTime = File.GetLastWriteTime(razorPage.DirectiveTemplatePath);
+ if (lastWriteTime > template.LastModified)
+ ReloadTemplate(template);
+ }
+ if (razorPage.TemplatePath != null
+ && this.PageTemplates.TryGetValue(razorPage.TemplatePath, out template))
+ {
+ lastWriteTime = File.GetLastWriteTime(razorPage.TemplatePath);
+ if (lastWriteTime > template.LastModified)
+ ReloadTemplate(template);
+ }
+ }
+
+ private void ReloadTemplate(RazorPage template)
+ {
+ var contents = File.ReadAllText(template.FilePath);
+ foreach (var markdownReplaceToken in MarkdownReplaceTokens)
+ {
+ contents = contents.Replace(markdownReplaceToken.Key, markdownReplaceToken.Value);
+ }
+ template.Reload(contents);
+ }
+
+ private RazorPage GetViewPageByResponse(object dto, IHttpRequest httpRequest)
+ {
+ var httpResult = dto as IHttpResult;
+ if (httpResult != null)
+ {
+ //If TemplateName was specified don't look for anything else.
+ if (httpResult.TemplateName != null)
+ return GetViewPage(httpResult.TemplateName);
+
+ dto = httpResult.Response;
+ }
+ if (dto != null)
+ {
+ var responseTypeName = dto.GetType().Name;
+ var markdownPage = GetViewPage(responseTypeName);
+ if (markdownPage != null) return markdownPage;
+ }
+
+ return httpRequest != null ? GetViewPage(httpRequest.OperationName) : null;
+ }
+
+ public RazorPage GetViewPage(string pageName)
+ {
+ RazorPage razorPage;
+
+ ViewPages.TryGetValue(pageName, out razorPage);
+ if (razorPage != null) return razorPage;
+
+ ViewSharedPages.TryGetValue(pageName, out razorPage);
+ return razorPage;
+ }
+
+ private void RegisterRazorPages(string razorSearchPath)
+ {
+ foreach (var page in FindRazorPagesFn(razorSearchPath))
+ {
+ AddPage(page);
+ }
+ }
+
+ public void AddPage(RazorPage page)
+ {
+ try
+ {
+ page.Prepare();
+ switch (page.PageType)
+ {
+ case RazorPageType.ViewPage:
+ ViewPages.Add(page.Name, page);
+ break;
+ case RazorPageType.SharedViewPage:
+ ViewSharedPages.Add(page.Name, page);
+ break;
+ case RazorPageType.ContentPage:
+ ContentPages.Add(page.FilePath.WithoutExtension(), page);
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Razor AddViewPage() page.Prepare(): " + ex.Message, ex);
+ }
+
+ var templatePath = page.TemplatePath;
+ if (page.TemplatePath == null) return;
+
+ if (PageTemplates.ContainsKey(templatePath)) return;
+
+ AddTemplate(templatePath, File.ReadAllText(templatePath));
+ }
+
+ public RazorPage AddTemplate(string templatePath, string templateContents)
+ {
+ var templateFile = new FileInfo(templatePath);
+ var templateName = templateFile.FullName.WithoutExtension();
+
+ foreach (var markdownReplaceToken in MarkdownReplaceTokens)
+ {
+ templateContents = templateContents.Replace(markdownReplaceToken.Key, markdownReplaceToken.Value);
+ }
+
+ var template = new RazorPage(this, templatePath, templateName, templateContents, RazorPageType.Template) {
+ LastModified = templateFile.LastWriteTime,
+ };
+ PageTemplates.Add(templatePath, template);
+ try
+ {
+ template.Prepare();
+ return template;
+ }
+ catch (Exception ex)
+ {
+ Log.Error("AddViewPage() template.Prepare(): " + ex.Message, ex);
+ return null;
+ }
+ }
+
+ public RazorPage GetContentPage(string pageFilePath)
+ {
+ RazorPage razorPage;
+ ContentPages.TryGetValue(pageFilePath, out razorPage);
+ return razorPage;
+ }
+
+ public string GetTemplate(string name)
+ {
+ Console.WriteLine("GetTemplate(): " + name);
+ RazorPage template;
+ PageTemplates.TryGetValue(name, out template);
+ return template != null ? template.Contents : null;
+ }
+
+ public ITemplate CreateInstance(Type type)
+ {
+ Console.WriteLine("CreateInstance(): " + type.Name);
+ var instance = ReflectionUtils.CreateInstance(type);
+
+ var templatePage = instance as ITemplatePage;
+ if (templatePage != null)
+ {
+ templatePage.AppHost = AppHost;
+ }
+
+ var template = (ITemplate)instance;
+ return template;
+ }
+ }
+}
View
22 src/RazorEngine/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("RazorEngine: Core")]
+[assembly: AssemblyDescription("Provides templating services using the Razor parser and code generator.")]
+
+[assembly: Guid("e38fdd39-0ae9-4c67-af72-79191d7a1f85")]
+
+#if DEBUG
+[assembly: AssemblyConfiguration("Debug")]
+#else
+[assembly: AssemblyConfiguration("Release")]
+#endif
+
+[assembly: AssemblyCompany("RazorEngine Project")]
+[assembly: AssemblyProduct("RazorEngine")]
+[assembly: AssemblyCopyright("Copyright © RazorEngine Project 2010")]
+
+[assembly: ComVisible(false)]
+
+[assembly: AssemblyVersion("2.1.*")]
+[assembly: AssemblyFileVersion("2.1.0.0")]
View
237 src/RazorEngine/Razor.cs
@@ -0,0 +1,237 @@
+namespace RazorEngine
+{
+ using System;
+ using System.Collections.Concurrent;
+ using System.Collections.Generic;
+
+ using Compilation;
+ using Configuration;
+ using Templating;
+
+ /// <summary>
+ /// Provides quick access to template services.
+ /// </summary>
+ public static class Razor
+ {
+ #region Constructor
+ /// <summary>
+ /// Initialises the <see cref="Razor"/> type.
+ /// </summary>
+ static Razor()
+ {
+ Services = new ConcurrentDictionary<string, TemplateService>();
+ Configure();
+ }
+ #endregion
+
+ #region Properties
+ /// <summary>
+ /// Gets the compiler service factory.
+ /// </summary>
+ internal static ICompilerServiceFactory CompilerServiceFactory { get; private set; }
+
+ /// <summary>
+ /// Gets the default template service.
+ /// </summary>
+ public static TemplateService DefaultTemplateService { get; private set; }
+
+ /// <summary>
+ /// Gets the collection of configured services.
+ /// </summary>
+ public static IDictionary<string, TemplateService> Services { get; private set; }
+ #endregion
+
+ #region Methods
+ /// <summary>
+ /// Adds a resolver used to resolve named template content.
+ /// </summary>
+ /// <param name="resolver">The resolver to add.</param>
+ public static void AddResolver(ITemplateResolver resolver)
+ {
+ DefaultTemplateService.AddResolver(resolver);
+ }
+
+ /// <summary>
+ /// Adds a resolver used to resolve named template content.
+ /// </summary>
+ /// <param name="resolverDelegate">The resolver delegate to add.</param>
+ public static void AddResolver(Func<string, string> resolverDelegate)
+ {
+ DefaultTemplateService.AddResolver(resolverDelegate);
+ }
+
+ /// <summary>
+ /// Pre-compiles the specified template and caches it using the specified name.
+ /// </summary>
+ /// <param name="template">The template to precompile.</param>
+ /// <param name="name">The cache name for the template.</param>
+ public static void Compile(string template, string name)
+ {
+ DefaultTemplateService.CompileWithAnonymous(template, name);
+ }
+
+ /// <summary>
+ /// Pre-compiles the specified template and caches it using the specified name.
+ /// </summary>
+ /// <param name="template">The template to precompile.</param>
+ /// <param name="modelType">The type of model used in the template.</param>
+ /// <param name="name">The cache name for the template.</param>
+ public static void Compile(string template, Type modelType, string name)
+ {
+ DefaultTemplateService.Compile(template, modelType, name);
+ }
+
+ /// <summary>
+ /// Pre-compiles the specified template and caches it using the specified name.
+ /// This method should be used when an anonymous model is used in the template.
+ /// </summary>
+ /// <param name="template">The template to precompile.</param>
+ /// <param name="name">The cache name for the template.</param>
+ public static void CompileWithAnonymous(string template, string name)
+ {
+ DefaultTemplateService.CompileWithAnonymous(template, name);
+ }
+
+ /// <summary>
+ /// Configures the templating engine.
+ /// </summary>
+ private static void Configure()
+ {
+ var config = RazorEngineConfigurationSection.GetConfiguration();
+ if (config != null)
+ {
+ if (!string.IsNullOrWhiteSpace(config.Factory))
+ SetCompilerServiceFactory(config.Factory);
+ else
+ CompilerServiceFactory = new DefaultCompilerServiceFactory();
+
+ if (config.TemplateServices.Count > 0)
+ {
+ string @default = string.IsNullOrWhiteSpace(config.TemplateServices.Default)
+ ? null
+ : config.TemplateServices.Default;
+
+ foreach (TemplateServiceConfigurationElement serviceConfig in config.TemplateServices)
+ {
+ string name = serviceConfig.Name;
+ var service = ConfigurationServices.CreateTemplateService(serviceConfig);;
+ ConfigurationServices.AddNamespaces(service, config.Namespaces);
+
+ if (name == @default)
+ DefaultTemplateService = service;
+
+ Services.Add(name, service);
+ }
+ }
+
+ if (DefaultTemplateService == null)
+ {
+ DefaultTemplateService = new TemplateService(CompilerServiceFactory.CreateCompilerService());
+ ConfigurationServices.AddNamespaces(DefaultTemplateService, config.Namespaces);
+ }
+
+ if (!string.IsNullOrWhiteSpace(config.Activator))
+ DefaultTemplateService.SetActivator(ConfigurationServices.CreateInstance<IActivator>(config.Activator));
+ }
+ else
+ {
+ ConfigureDefault();
+ }
+ }
+
+ /// <summary>
+ /// Applies a default configuration.
+ /// </summary>
+ private static void ConfigureDefault()
+ {
+ CompilerServiceFactory = new DefaultCompilerServiceFactory();
+ var service = CompilerServiceFactory.CreateCompilerService();
+
+ DefaultTemplateService = new TemplateService(service);
+ }
+
+ /// <summary>
+ /// Parses the given template and returns the result.
+ /// </summary>
+ /// <param name="template">The template to parse.</param>
+ /// <param name="name">[Optional] The name of the template. This is used to cache the template.</param>
+ /// <returns>The string result of the parsed template.</returns>
+ public static string Parse(string template, string name = null)
+ {
+ return DefaultTemplateService.Parse(template, name);
+ }
+
+ /// <summary>
+ /// Parses the given template and returns the result.
+ /// </summary>
+ /// <typeparam name="T">The model type.</typeparam>
+ /// <param name="template">The template to parse.</param>
+ /// <param name="model">The model.</param>
+ /// <param name="name">[Optional] The name of the template. This is used to cache the template.</param>
+ /// <returns>The string result of the parsed template.</returns>
+ public static string Parse<T>(string template, T model, string name = null)
+ {
+ return DefaultTemplateService.Parse<T>(template, model, name);
+ }
+
+ /// <summary>
+ /// Runs the template with the specified name.
+ /// </summary>
+ /// <param name="name">The name of the template to run.</param>
+ /// <returns>The result of the template.</returns>
+ public static string Run(string name)
+ {
+ return DefaultTemplateService.Run(name);
+ }
+
+ /// <summary>
+ /// Runs the template with the specified name.
+ /// </summary>
+ /// <typeparam name="T">The model type.</typeparam>
+ /// <param name="model">The model.</param>
+ /// <param name="name">The name of the template to run.</param>
+ /// <returns>The result of the template.</returns>
+ public static string Run<T>(T model, string name)
+ {
+ return DefaultTemplateService.Run<T>(model, name);
+ }
+
+ /// <summary>
+ /// Sets the activator used to create types.
+ /// </summary>
+ /// <param name="activator">The activator to use.</param>
+ public static void SetActivator(IActivator activator)
+ {
+ DefaultTemplateService.SetActivator(activator);
+ }
+
+ /// <summary>
+ /// Sets the activator delegate used to create types.
+ /// </summary>
+ /// <param name="activator">The activator delegate to use.</param>
+ public static void SetActivator(Func<Type, ITemplate> activator)
+ {
+ DefaultTemplateService.SetActivator(activator);
+ }
+
+ /// <summary>
+ /// Sets the compiler factory.
+ /// </summary>
+ /// <param name="typeName">The factory type.</param>
+ private static void SetCompilerServiceFactory(string typeName)
+ {
+ var factory = ConfigurationServices.CreateInstance<ICompilerServiceFactory>(typeName);
+ CompilerServiceFactory = factory;
+ }
+
+ /// <summary>
+ /// Sets the template base type.
+ /// </summary>
+ /// <param name="type">The template base type.</param>
+ public static void SetTemplateBase(Type type)
+ {
+ DefaultTemplateService.SetTemplateBase(type);
+ }
+ #endregion
+ }
+}
View
137 src/RazorEngine/RazorEngine.csproj
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{64128C85-B9AF-4B4C-BE83-04983EF7F8C9}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>RazorEngine</RootNamespace>
+ <AssemblyName>RazorEngine</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ <SccProjectName>
+ </SccProjectName>
+ <SccLocalPath>
+ </SccLocalPath>
+ <SccAuxPath>
+ </SccAuxPath>
+ <SccProvider>
+ </SccProvider>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignAssembly>true</SignAssembly>
+ </PropertyGroup>
+ <PropertyGroup>
+ <AssemblyOriginatorKeyFile>
+ </AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="ServiceStack.Text">
+ <HintPath>..\..\lib\tests\ServiceStack.Text.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Configuration" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Web">
+ <HintPath>..\..\..\..\Windows\Microsoft.NET\Framework\v2.0.50727\System.Web.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Web.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\lib\System.Web.Razor.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Compilation\CompilerServiceBase.cs" />
+ <Compile Include="Compilation\CompilerServices.cs" />
+ <Compile Include="Compilation\CSharp\CSharpDirectCompilerService.cs" />
+ <Compile Include="Compilation\CSharp\CSharpRazorCodeGenerator.cs" />
+ <Compile Include="Compilation\CSharp\CSharpRazorCodeLanguage.cs" />
+ <Compile Include="Compilation\DirectCompilerServiceBase.cs" />
+ <Compile Include="Compilation\DefaultCompilerServiceFactory.cs" />
+ <Compile Include="Compilation\ICompilerService.cs" />
+ <Compile Include="IRazorTemplate.cs" />
+ <Compile Include="MicrosoftCSharpReferenceStub.cs" />
+ <Compile Include="Compilation\RazorDynamicObject.cs" />
+ <Compile Include="Compilation\HasDynamicModelAttribute.cs" />
+ <Compile Include="Compilation\TypeContext.cs" />
+ <Compile Include="Configuration\ConfigurationServices.cs" />
+ <Compile Include="Configuration\NamespaceConfigurationElement.cs" />
+ <Compile Include="Configuration\NamespaceConfigurationElementCollection.cs" />
+ <Compile Include="Configuration\RazorEngineConfigurationSection.cs" />
+ <Compile Include="Configuration\TemplateServiceConfigurationElement.cs" />
+ <Compile Include="Configuration\TemplateServiceConfigurationElementCollection.cs" />
+ <Compile Include="Compilation\ICompilerServiceFactory.cs" />
+ <Compile Include="Language.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Razor.cs" />
+ <Compile Include="MvcRazorFormat.cs" />
+ <Compile Include="RazorHandler.cs" />
+ <Compile Include="RazorPage.cs" />
+ <Compile Include="RazorPageBase.cs" />
+ <Compile Include="Templating\DefaultActivator.cs" />
+ <Compile Include="Templating\DelegateActivator.cs" />
+ <Compile Include="Templating\DelegateTemplateResolver.cs" />
+ <Compile Include="Templating\IActivator.cs" />
+ <Compile Include="Templating\ITemplate.cs" />
+ <Compile Include="Templating\ITemplateOfT.cs" />
+ <Compile Include="Templating\ITemplateResolver.cs" />
+ <Compile Include="Templating\RequireNamespacesAttribute.cs" />
+ <Compile Include="Templating\TemplateBase.cs" />
+ <Compile Include="Templating\TemplateBaseOfT.cs" />
+ <Compile Include="Templating\TemplateCompilationException.cs" />
+ <Compile Include="Templating\TemplateParsingException.cs" />
+ <Compile Include="Templating\TemplateService.cs" />
+ <Compile Include="Templating\TemplateService.ServiceStack.cs" />
+ <Compile Include="Templating\TemplateServiceFactory.cs" />
+ <Compile Include="Templating\TemplateWriter.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\ServiceStack.Common\ServiceStack.Common.csproj">
+ <Project>{982416DB-C143-4028-A0C3-CF41892D18D3}</Project>
+ <Name>ServiceStack.Common</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ServiceStack.Interfaces\ServiceStack.Interfaces.csproj">
+ <Project>{42E1C8C0-A163-44CC-92B1-8F416F2C0B01}</Project>
+ <Name>ServiceStack.Interfaces</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
+ <Project>{680A1709-25EB-4D52-A87F-EE03FFD94BAA}</Project>
+ <Name>ServiceStack</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
48 src/RazorEngine/RazorHandler.cs
@@ -0,0 +1,48 @@
+using System.Net;
+using ServiceStack.ServiceHost;
+using ServiceStack.Text;
+using ServiceStack.WebHost.Endpoints.Support;
+
+namespace RazorEngine
+{
+ public class RazorHandler : EndpointHandlerBase
+ {
+ public MvcRazorFormat MvcRazorFormat { get; set; }
+ public RazorPage RazorPage { get; set; }
+
+ public string PathInfo { get; set; }
+ public string FilePath { get; set; }
+
+ public override void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
+ {
+ var contentPage = RazorPage;
+ if (contentPage == null)
+ {
+ var pageFilePath = this.FilePath.WithoutExtension();
+ contentPage = MvcRazorFormat.GetContentPage(pageFilePath);
+ }
+ if (contentPage == null)
+ {
+ httpRes.StatusCode = (int)HttpStatusCode.NotFound;
+ return;
+ }
+
+ MvcRazorFormat.ReloadModifiedPageAndTemplates(contentPage);
+
+ if (httpReq.DidReturn304NotModified(contentPage.GetLastModified(), httpRes))
+ return;
+
+ MvcRazorFormat.ProcessRazorPage(httpReq, contentPage, null, httpRes);
+ }
+
+ public override object CreateRequest(IHttpRequest request, string operationName)
+ {
+ return null;
+ }
+
+ public override object GetResponse(IHttpRequest httpReq, object request)
+ {
+ return null;
+ }
+ }
+}
View
127 src/RazorEngine/RazorPage.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using ServiceStack.WebHost.EndPoints.Support.Markdown;
+
+namespace RazorEngine
+{
+ public class RazorPage
+ {
+ public MvcRazorFormat RazorFormat { get; set; }
+
+ public string FilePath { get; set; }
+ public string Name { get; set; }
+ public string Contents { get; set; }
+
+ public RazorPageType PageType { get; set; }
+ public string TemplatePath { get; set; }
+ public string DirectiveTemplatePath { get; set; }
+ public DateTime? LastModified { get; set; }
+ public List<IExpirable> Dependents { get; private set; }
+
+ public const string ModelName = "Model";
+
+ public RazorPage()
+ {
+ this.Dependents = new List<IExpirable>();
+ }
+
+ public RazorPage(MvcRazorFormat razorFormat, string fullPath, string name, string contents)
+ : this(razorFormat, fullPath, name, contents, RazorPageType.ViewPage)
+ {
+ }
+
+ public RazorPage(MvcRazorFormat razorFormat, string fullPath, string name, string contents, RazorPageType pageType)
+ : this()
+ {
+ RazorFormat = razorFormat;
+ FilePath = fullPath;
+ Name = name;
+ Contents = contents;
+ PageType = pageType;
+ }
+
+ public DateTime? GetLastModified()
+ {
+ //if (!hasCompletedFirstRun) return null;
+ var lastModified = this.LastModified;
+ foreach (var expirable in this.Dependents)
+ {
+ if (!expirable.LastModified.HasValue) continue;
+ if (!lastModified.HasValue || expirable.LastModified > lastModified)
+ {
+ lastModified = expirable.LastModified;
+ }
+ }
+ return lastModified;
+ }
+
+ public string GetTemplatePath()
+ {
+ return this.DirectiveTemplatePath ?? this.TemplatePath;
+ }
+
+ public string PageName
+ {
+ get
+ {
+ return this.PageType == RazorPageType.Template
+ ? this.FilePath
+ : this.Name;
+ }
+ }
+
+ public void Prepare()
+ {
+ Razor.Compile(this.Contents, PageName);
+ }
+
+ private int timesRun;
+
+ private Exception initException;
+ readonly object readWriteLock = new object();
+ private bool isBusy;
+ public void Reload()
+ {
+ var contents = File.ReadAllText(this.FilePath);
+ Reload(contents);
+ }
+
+ public void Reload(string contents)
+ {
+ var fi = new FileInfo(this.FilePath);
+ var lastModified = fi.LastWriteTime;
+ lock (readWriteLock)
+ {
+ try
+ {
+ isBusy = true;
+
+ this.Contents = contents;
+ foreach (var markdownReplaceToken in RazorFormat.MarkdownReplaceTokens)
+ {
+ this.Contents = this.Contents.Replace(markdownReplaceToken.Key, markdownReplaceToken.Value);
+ }
+
+ this.LastModified = lastModified;
+ initException = null;
+ timesRun = 0;
+ Prepare();
+ }
+ catch (Exception ex)
+ {
+ initException = ex;
+ }
+ isBusy = false;
+ Monitor.PulseAll(readWriteLock);
+ }
+ }
+
+ public string RenderToString(object model)
+ {
+ var html = Razor.Run(model, this.PageName);
+ return html;
+ }
+ }
+}
View
26 src/RazorEngine/RazorPageBase.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using RazorEngine.Templating;
+using ServiceStack.WebHost.Endpoints;
+
+namespace RazorEngine
+{
+ public abstract class RazorPageBase<TModel> : TemplateBase<TModel>, IRazorTemplate
+ {
+ public IAppHost AppHost { get; set; }
+
+ public Dictionary<string, object> ScopeArgs { get; set; }
+
+ public string Layout { get; set; }
+
+ protected RazorPageBase()
+ {
+ this.ScopeArgs = new Dictionary<string, object>();
+ }
+
+ public T TryResolve<T>()
+ {
+ return this.AppHost.TryResolve<T>();
+ }
+ }
+}
View
25 src/RazorEngine/Templating/DefaultActivator.cs