├── src ├── RazorTemplates.v11.suo ├── Shared │ ├── RazorTemplates.snk │ └── RazorTemplates.Public.snk ├── RazorTemplates.Core │ ├── RazorTemplates.snk │ ├── TemplateCompilationLanguage.cs │ ├── TemplateCompilationResult.cs │ ├── ITemplate`1.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── RazorTemplates.Core.nuspec │ ├── TemplateBase`1.cs │ ├── Infrastructure │ │ ├── PositionTagged.cs │ │ └── AttributeValue.cs │ ├── Template`1.cs │ ├── TemplateBase.cs │ ├── Template.cs │ ├── TemplateCompilationException.cs │ ├── RazorTemplates.Core.csproj │ ├── TemplateDescription`2.cs │ └── TemplateCompiler.cs ├── RazorTemplates.Tests │ ├── TestModel.cs │ ├── Template.cshtml │ ├── packages.config │ ├── Bugs │ │ ├── UsingLinqInTemlate.cs │ │ └── AddingExtraPeriodToSomeUrls.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── TemplateTests.cs │ └── RazorTemplates.Tests.csproj ├── RazorTemplates.Samples │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── RazorTemplates.Samples.csproj ├── RazorTemplates.sln └── RazorTemplates.sln.DotSettings.user ├── .gitignore ├── README.md └── license.txt /src/RazorTemplates.v11.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volkovku/RazorTemplates/HEAD/src/RazorTemplates.v11.suo -------------------------------------------------------------------------------- /src/Shared/RazorTemplates.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volkovku/RazorTemplates/HEAD/src/Shared/RazorTemplates.snk -------------------------------------------------------------------------------- /src/Shared/RazorTemplates.Public.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volkovku/RazorTemplates/HEAD/src/Shared/RazorTemplates.Public.snk -------------------------------------------------------------------------------- /src/RazorTemplates.Core/RazorTemplates.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volkovku/RazorTemplates/HEAD/src/RazorTemplates.Core/RazorTemplates.snk -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/TestModel.cs: -------------------------------------------------------------------------------- 1 | namespace RazorTemplates.Tests 2 | { 3 | public class TestModel 4 | { 5 | public string Message { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/Template.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Core.TemplateBase 2 | 3 | @Model.Message 4 | @JsonConvert.SerializeObject(Model) 5 | -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | bin 3 | obj 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | # nuget 9 | **/packages/* 10 | !**/packages/build/ 11 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateCompilationLanguage.cs: -------------------------------------------------------------------------------- 1 | namespace RazorTemplates.Core 2 | { 3 | public enum TemplateCompilationLanguage 4 | { 5 | CSharp, 6 | VisualBasic 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateCompilationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RazorTemplates.Core 4 | { 5 | /// 6 | /// Represents result of template compilation. 7 | /// 8 | internal struct TemplateCompilationResult 9 | { 10 | /// 11 | /// A type of compiled temlate (inherit from TemplateBase). 12 | /// 13 | public Type Type; 14 | 15 | /// 16 | /// A source code of compiled template. 17 | /// 18 | public string SourceCode; 19 | } 20 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Core/ITemplate`1.cs: -------------------------------------------------------------------------------- 1 | namespace RazorTemplates.Core 2 | { 3 | /// 4 | /// Describes an interface of template. 5 | /// 6 | public interface ITemplate 7 | { 8 | /// 9 | /// Returns generated source code for this template. 10 | /// 11 | string SourceCode { get; } 12 | 13 | /// 14 | /// Renders templates with data from specified model. 15 | /// 16 | /// A model data. 17 | /// A rendered content. 18 | string Render(TModel model); 19 | } 20 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyVersion("1.2.1.0")] 5 | [assembly: AssemblyFileVersion("1.2.1.0")] 6 | 7 | [assembly: AssemblyTitle("RazorTemplates.Core")] 8 | [assembly: AssemblyDescription("RazorTemplates - Core Framework")] 9 | [assembly: AssemblyCompany("RazorTemplates Project")] 10 | [assembly: AssemblyProduct("RazorTemplates")] 11 | [assembly: AssemblyCopyright("Copyright © RazorTemplates Project 2012")] 12 | 13 | #if DEBUG 14 | [assembly: AssemblyConfiguration("Debug")] 15 | #else 16 | [assembly: AssemblyConfiguration("Release")] 17 | #endif 18 | 19 | [assembly: ComVisible(false)] 20 | [assembly: Guid("ef672b69-7d27-468a-8121-8cfab9130dec")] -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/Bugs/UsingLinqInTemlate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using RazorTemplates.Core; 4 | 5 | namespace RazorTemplates.Tests.Bugs 6 | { 7 | [TestClass] 8 | public class UsingLinqInTemlate 9 | { 10 | [TestMethod] 11 | public void ItShouldAllowToUseLinqInTemplates() 12 | { 13 | var template = Template 14 | .WithBaseType() 15 | .AddNamespace("System.Linq") 16 | .Compile>("Hello @(Model.First())"); 17 | 18 | var result = template.Render(new List { "Hello", "World" }); 19 | Assert.AreEqual("Hello Hello", result); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/Bugs/AddingExtraPeriodToSomeUrls.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using RazorTemplates.Core; 3 | 4 | namespace RazorTemplates.Tests.Bugs 5 | { 6 | [TestClass] 7 | public class AddingExtraPeriodToSomeUrls 8 | { 9 | [TestMethod] 10 | public void ItShouldNotAddExtraPeriodToUrls() 11 | { 12 | var images = new[] { "jazz", "coffee", "box", "cup" }; 13 | 14 | const string templateSource = ""; 15 | var template = Template.Compile(templateSource); 16 | 17 | foreach (var image in images) 18 | { 19 | var expected = string.Format("", image); 20 | var actual = template.Render(image); 21 | 22 | Assert.AreEqual(expected, actual); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Core/RazorTemplates.Core.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RazorTemplates 5 | 1.2.1 6 | RazorTemplates 7 | volkovku 8 | volkovku 9 | https://opensource.org/licenses/MIT 10 | https://github.com/volkovku/RazorTemplates 11 | https://www.nuget.org/ 12 | false 13 | Open source templating engine based on Microsoft's Razor parsing engine. Thread safe. Allows run Razor templates outside ASP.Net MVC Projects. 14 | Bugfix: loading of additional assemblies 15 | Copyright © RazorTemplates Project 2012 16 | razor razorengine templating razortemplates 17 | 18 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateBase`1.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace RazorTemplates.Core 4 | { 5 | /// 6 | /// Represents a base class for generated templates. 7 | /// 8 | public abstract class TemplateBase : TemplateBase 9 | { 10 | private readonly StringBuilder _buffer = new StringBuilder(); 11 | 12 | /// 13 | /// Gets or sets dynamic model which data should be rendered. 14 | /// 15 | public new virtual TModel Model 16 | { 17 | get { return (TModel)base.Model; } 18 | set { base.Model = value; } 19 | } 20 | 21 | /// 22 | /// Renders specified model. 23 | /// 24 | public virtual string Render(TModel model) 25 | { 26 | Model = model; 27 | Execute(); 28 | return _buffer.ToString(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Core/Infrastructure/PositionTagged.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RazorTemplates.Core.Infrastructure 4 | { 5 | /// 6 | /// Position tagged value for support RazorEngine 2.0 7 | /// 8 | public class PositionTagged 9 | { 10 | /// 11 | /// Gets tag position. 12 | /// 13 | public int Position { get; private set; } 14 | 15 | /// 16 | /// Gets tag value. 17 | /// 18 | public T Value { get; private set; } 19 | 20 | /// 21 | /// Initializes a new instance of PositionTagged class. 22 | /// 23 | public PositionTagged(T value, int offset) 24 | { 25 | Position = offset; 26 | Value = value; 27 | } 28 | 29 | /// 30 | /// Converts tuple to PositionTagged object. 31 | /// 32 | public static implicit operator PositionTagged(Tuple value) 33 | { 34 | return new PositionTagged(value.Item1, value.Item2); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Samples/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using RazorTemplates.Core; 3 | 4 | namespace RazorTemplates.Samples 5 | { 6 | class Program 7 | { 8 | static void Main() 9 | { 10 | SimpleTemplate(); 11 | TemplateWithCustomNamespaces(); 12 | 13 | Console.ReadLine(); 14 | } 15 | 16 | public static void SimpleTemplate() 17 | { 18 | var template = Template.Compile("Hello @Model.Name!"); 19 | Console.WriteLine(template.Render(new { Name = "world" })); 20 | } 21 | 22 | public static void TemplateWithCustomNamespaces() 23 | { 24 | var template = Template 25 | .WithBaseType() 26 | .AddNamespace("RazorTemplates.Samples") 27 | .Compile(@"There is @Model.Apples @Plural.Form(Model.Apples, new [] { ""apple"", ""apples"" }) in the box."); 28 | 29 | Console.WriteLine(template.Render(new { Apples = 1 })); 30 | Console.WriteLine(template.Render(new { Apples = 2 })); 31 | } 32 | } 33 | 34 | public static class Plural 35 | { 36 | public static string Form(int value, string[] forms) 37 | { 38 | var form = value == 1 ? 0 : 1; 39 | return forms[form]; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RazorTemplates.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RazorTemplates.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("94d45d51-16b1-4d28-a52d-46d4fb7ad5e6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/RazorTemplates.Samples/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RazorTemplates.Samples")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RazorTemplates.Samples")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2b5bdd7e-aa83-49e6-98d6-9dc3050f7202")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RazorTemplates 2 | ============== 3 | 4 | Open source templating engine based on Microsoft's Razor parsing engine. Thread safe. Allows to run Razor templates outside of ASP.NET projects. 5 | 6 | ## Install ## 7 | 8 | To install RazorTemplates, run the following command in the [Package Manager Console](http://docs.nuget.org/docs/start-here/using-the-package-manager-console): 9 | ``` 10 | PM> Install-Package RazorTemplates 11 | ``` 12 | 13 | ## Get Started ## 14 | 15 | Using the library is as simple as two lines of code: 16 | 17 | ```csharp 18 | using RazorTemplates.Core; 19 | 20 | var template = Template.Compile("Hello @Model.Name!"); 21 | Console.WriteLine(template.Render(new { Name = "World" })); 22 | ``` 23 | 24 | ## Extending with custom methods ## 25 | 26 | You can extend templates by including required namespaces: 27 | 28 | ```csharp 29 | using System; 30 | using RazorTemplates.Core; 31 | 32 | namespace TestApplication 33 | { 34 | class Program 35 | { 36 | static void Main(string[] args) 37 | { 38 | var template = Template 39 | .WithBaseType() 40 | .AddNamespace("TestApplication") 41 | .Compile(@"@Model.Apples @Plural.Form(Model.Apples, new [] { ""apple"", ""apples"" }) in the box."); 42 | 43 | Console.WriteLine(template.Render(new { Apples = 1 })); 44 | Console.WriteLine(template.Render(new { Apples = 2 })); 45 | } 46 | } 47 | 48 | public static class Plural 49 | { 50 | public static string Form(int value, string[] forms) 51 | { 52 | var form = value == 1 ? 0 : 1; 53 | return forms[form]; 54 | } 55 | } 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/Infrastructure/AttributeValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RazorTemplates.Core.Infrastructure 4 | { 5 | /// 6 | /// Html attribute value for support RazorEngine 2.0 7 | /// 8 | public class AttributeValue 9 | { 10 | /// 11 | /// Gets a prefix of attribute value. 12 | /// 13 | public PositionTagged Prefix { get; private set; } 14 | 15 | /// 16 | /// Gets an attribute value. 17 | /// 18 | public PositionTagged Value { get; private set; } 19 | 20 | /// 21 | /// Gets a flag which determines is attribute value is literal. 22 | /// 23 | public bool Literal { get; private set; } 24 | 25 | /// 26 | /// Initializes a new instance of AttributeValue class. 27 | /// 28 | public AttributeValue(PositionTagged prefix, PositionTagged value, bool literal) 29 | { 30 | Prefix = prefix; 31 | Value = value; 32 | Literal = literal; 33 | } 34 | 35 | /// 36 | /// Converts tuples to AttributeValue object. 37 | /// 38 | public static implicit operator AttributeValue(Tuple, Tuple, bool> value) 39 | { 40 | return new AttributeValue(value.Item1, value.Item2, value.Item3); 41 | } 42 | 43 | /// 44 | /// Converts tuples to AttributeValue object. 45 | /// 46 | public static implicit operator AttributeValue(Tuple, Tuple, bool> value) 47 | { 48 | return new AttributeValue(value.Item1, new PositionTagged(value.Item2.Item1, value.Item2.Item2), value.Item3); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Core/Template`1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Dynamic; 5 | 6 | namespace RazorTemplates.Core 7 | { 8 | internal class Template : ITemplate where T : TemplateBase 9 | { 10 | private readonly Type _templateType; 11 | private readonly Action _initializer; 12 | private readonly string _sourceCode; 13 | 14 | internal Template(Type templateType, string sourceCode, Action initializer) 15 | { 16 | _templateType = templateType; 17 | _sourceCode = sourceCode; 18 | _initializer = initializer; 19 | } 20 | 21 | public string SourceCode 22 | { 23 | get { return _sourceCode; } 24 | } 25 | 26 | public string Render(TModel model) 27 | { 28 | var template = CreateTemplateInstance(); 29 | 30 | if (!ReferenceEquals(null, model) 31 | && model.GetType().Name.StartsWith("<>f__AnonymousType")) 32 | return template.Render(CreateExpandoObject(model)); 33 | 34 | return template.Render(model); 35 | } 36 | 37 | protected T CreateTemplateInstance() 38 | { 39 | var instance = (T)Activator.CreateInstance(_templateType); 40 | 41 | if (_initializer != null) 42 | _initializer(instance); 43 | return instance; 44 | } 45 | 46 | private static ExpandoObject CreateExpandoObject(object anonymousObject) 47 | { 48 | var expandoObject = anonymousObject as ExpandoObject; 49 | if (expandoObject != null) return expandoObject; 50 | 51 | var anonymousDictionary = new Dictionary(); 52 | if (anonymousObject != null) 53 | foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(anonymousObject)) 54 | anonymousDictionary.Add(property.Name, property.GetValue(anonymousObject)); 55 | 56 | IDictionary expando = new ExpandoObject(); 57 | foreach (var item in anonymousDictionary) expando.Add(item); 58 | 59 | return (ExpandoObject)expando; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/RazorTemplates.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RazorTemplates.Core", "RazorTemplates.Core\RazorTemplates.Core.csproj", "{F4411488-D1A2-4A06-8891-E44C3BDCC4A8}" 5 | EndProject 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{93B59751-9388-451E-9EB9-36812C8276D5}" 7 | ProjectSection(SolutionItems) = preProject 8 | Shared\RazorTemplates.Public.snk = Shared\RazorTemplates.Public.snk 9 | Shared\RazorTemplates.snk = Shared\RazorTemplates.snk 10 | EndProjectSection 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RazorTemplates.Samples", "RazorTemplates.Samples\RazorTemplates.Samples.csproj", "{522203D1-1AAC-4AA1-A796-1ABD8F61F9FA}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RazorTemplates.Tests", "RazorTemplates.Tests\RazorTemplates.Tests.csproj", "{1D575AD0-6C38-466A-ADB2-E550AFB94E43}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {F4411488-D1A2-4A06-8891-E44C3BDCC4A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F4411488-D1A2-4A06-8891-E44C3BDCC4A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F4411488-D1A2-4A06-8891-E44C3BDCC4A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F4411488-D1A2-4A06-8891-E44C3BDCC4A8}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {522203D1-1AAC-4AA1-A796-1ABD8F61F9FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {522203D1-1AAC-4AA1-A796-1ABD8F61F9FA}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {522203D1-1AAC-4AA1-A796-1ABD8F61F9FA}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {522203D1-1AAC-4AA1-A796-1ABD8F61F9FA}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {1D575AD0-6C38-466A-ADB2-E550AFB94E43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {1D575AD0-6C38-466A-ADB2-E550AFB94E43}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {1D575AD0-6C38-466A-ADB2-E550AFB94E43}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {1D575AD0-6C38-466A-ADB2-E550AFB94E43}.Release|Any CPU.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Microsoft Public License (Ms-PL) 2 | 3 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 4 | 5 | Definitions 6 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. 7 | A "contribution" is the original software, or any additions or changes to the software. 8 | A "contributor" is any person that distributes its contribution under this license. 9 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 10 | Grant of Rights 11 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 12 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 13 | Conditions and Limitations 14 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 15 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 16 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 17 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 18 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/TemplateTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | using System.IO; 4 | using System.Reflection; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using RazorTemplates.Core; 7 | 8 | namespace RazorTemplates.Tests 9 | { 10 | [TestClass] 11 | public class TemplateTests 12 | { 13 | [TestMethod] 14 | public void ItShouldRenderExpandoObjects() 15 | { 16 | dynamic expando = new ExpandoObject(); 17 | var template = Template.Compile("There is @Model.Count @Model.Item in the box."); 18 | 19 | expando.Count = 1; 20 | expando.Item = "apple"; 21 | 22 | Assert.AreEqual("There is 1 apple in the box.", template.Render(expando)); 23 | 24 | expando.Count = 2; 25 | expando.Item = "apples"; 26 | 27 | Assert.AreEqual("There is 2 apples in the box.", template.Render(expando)); 28 | } 29 | 30 | [TestMethod] 31 | public void ItShouldSupportAnonymousObjects() 32 | { 33 | var obj = new {Count = 1, Item = "apple"}; 34 | var template = Template.Compile("There is @Model.Count @Model.Item in the box."); 35 | 36 | Assert.AreEqual("There is 1 apple in the box.", template.Render(obj)); 37 | } 38 | 39 | [TestMethod] 40 | public void ItShouldUseTheTypedModel() 41 | { 42 | var templateStream = Assembly 43 | .GetExecutingAssembly() 44 | .GetManifestResourceStream("RazorTemplates.Tests.Template.cshtml"); 45 | 46 | var templateContent = new StreamReader(templateStream).ReadToEnd(); 47 | var template = Template 48 | .WithBaseType() 49 | .AddAssemblies("Newtonsoft.Json.dll") 50 | .AddNamespace("Newtonsoft.Json") 51 | .Compile(templateContent); 52 | var model = new TestModel { Message = "Hello world" }; 53 | 54 | Assert.AreEqual(model.Message + "\r\n{\"Message\":\"Hello world\"}\r\n", template.Render(model)); 55 | } 56 | 57 | [TestMethod] 58 | public void ItShouldLoadAssemblies() 59 | { 60 | var obj = new { Count = 1, Item = "apple" }; 61 | var template = Template 62 | .WithBaseType() 63 | .AddAssemblies("Newtonsoft.Json.dll") 64 | .AddNamespace("Newtonsoft.Json") 65 | .Compile("There is @Model.Count @Model.Item in the box. @JsonConvert.SerializeObject(@Model)"); 66 | 67 | Assert.AreEqual("There is 1 apple in the box. {\"Count\":1,\"Item\":\"apple\"}", template.Render(obj)); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateBase.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using RazorTemplates.Core.Infrastructure; 3 | 4 | namespace RazorTemplates.Core 5 | { 6 | /// 7 | /// Represents a base class for generated templates. 8 | /// 9 | public abstract class TemplateBase 10 | { 11 | private readonly StringBuilder _buffer = new StringBuilder(); 12 | 13 | /// 14 | /// Gets or sets dynamic model which data should be rendered. 15 | /// 16 | public virtual dynamic Model { get; set; } 17 | 18 | /// 19 | /// Renders specified model. 20 | /// 21 | public virtual string Render(object model) 22 | { 23 | Model = model; 24 | Execute(); 25 | return _buffer.ToString(); 26 | } 27 | 28 | /// 29 | /// A method which implemets by Razor engine. 30 | /// Produces sequance like: 31 | /// WriteLiteral("Hello "); 32 | /// Write(Model.Name); 33 | /// WriteLiteral("!"); 34 | /// 35 | public abstract void Execute(); 36 | 37 | /// 38 | /// Writes a string. 39 | /// 40 | protected void Write(string value) 41 | { 42 | _buffer.Append(value); 43 | } 44 | 45 | /// 46 | /// Writes a string representation of specified object. 47 | /// 48 | protected void Write(object value) 49 | { 50 | _buffer.Append(value); 51 | } 52 | 53 | /// 54 | /// Writes specified string. 55 | /// 56 | protected void WriteLiteral(string value) 57 | { 58 | _buffer.Append(value); 59 | } 60 | 61 | /// 62 | /// Razor 2.0 63 | /// Writes attribute in situations like <img src="@Model">. 64 | /// 65 | protected void WriteAttribute( 66 | string attribute, 67 | PositionTagged prefix, 68 | PositionTagged suffix, 69 | params AttributeValue[] values) 70 | { 71 | _buffer.Append(prefix.Value); 72 | 73 | if (values != null) 74 | { 75 | foreach (var attributeValue in values) 76 | { 77 | _buffer.Append(attributeValue.Prefix.Value); 78 | 79 | var value = attributeValue.Value.Value; 80 | if (value != null) 81 | { 82 | _buffer.Append(value); 83 | } 84 | } 85 | } 86 | 87 | _buffer.Append(suffix.Value); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Samples/RazorTemplates.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {522203D1-1AAC-4AA1-A796-1ABD8F61F9FA} 8 | Exe 9 | Properties 10 | RazorTemplates.Samples 11 | RazorTemplates.Samples 12 | v4.0 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {F4411488-D1A2-4A06-8891-E44C3BDCC4A8} 52 | RazorTemplates.Core 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/Template.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace RazorTemplates.Core 5 | { 6 | /// 7 | /// Represents an entry point for create templates. 8 | /// 9 | public static class Template 10 | { 11 | /// 12 | /// Gets or sets flag which determines is templates debugging info will 13 | /// included in output results. 14 | /// 15 | public static bool Debug 16 | { 17 | get { return TemplateCompiler.Debug; } 18 | set { TemplateCompiler.Debug = value; } 19 | } 20 | 21 | /// 22 | /// Gets or sets the language that will be used to compile templates. 23 | /// 24 | public static TemplateCompilationLanguage Language 25 | { 26 | get { return TemplateCompiler.Language; } 27 | set { TemplateCompiler.Language = value; } 28 | } 29 | 30 | /// 31 | /// Returns template created from specified source. 32 | /// 33 | public static ITemplate Compile(string source) 34 | { 35 | if (string.IsNullOrEmpty(source)) 36 | throw new ArgumentException( 37 | "Template source can't be null or empty string.", 38 | "source"); 39 | 40 | var compilationResult = TemplateCompiler.Compile( 41 | typeof(TemplateBase), 42 | source, 43 | Enumerable.Empty() /* assembly file names */, 44 | Enumerable.Empty() /* namespaces */, 45 | null /* compilation directory */); 46 | 47 | return new Template(compilationResult.Type, compilationResult.SourceCode, null); 48 | } 49 | 50 | /// 51 | /// Returns strong typed template created from specified source. 52 | /// 53 | public static ITemplate Compile(string source) 54 | { 55 | if (string.IsNullOrEmpty(source)) 56 | throw new ArgumentException( 57 | "Template source can't be null or empty string.", 58 | "source"); 59 | 60 | var compilationResult = TemplateCompiler.Compile( 61 | typeof (TemplateBase), 62 | source, 63 | Enumerable.Empty() /* assembly file names */, 64 | Enumerable.Empty() /* namespaces */, 65 | null /* compilation directory */); 66 | 67 | return new Template, TModel>(compilationResult.Type, compilationResult.SourceCode, null); 68 | } 69 | 70 | /// 71 | /// Creates template with specified base type. 72 | /// 73 | public static TemplateDescription WithBaseType(Action inializer = null) where T : TemplateBase 74 | { 75 | return new TemplateDescription(inializer); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateCompilationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace RazorTemplates.Core 8 | { 9 | /// 10 | /// Represents an exception which raises when template contains compilation errors. 11 | /// 12 | public class TemplateCompilationException : Exception 13 | { 14 | private readonly CompilerErrorCollection _errors; 15 | private readonly string _sourceCode; 16 | private readonly string _template; 17 | 18 | /// 19 | /// Initializes a new instance of TemplateCompilationException class. 20 | /// 21 | /// A collection of occured errors. 22 | /// A generated source code (available if debug mode is on). 23 | /// A template code. 24 | public TemplateCompilationException(CompilerErrorCollection errors, string sourceCode, string template) 25 | { 26 | _errors = errors; 27 | _sourceCode = sourceCode; 28 | _template = template; 29 | } 30 | 31 | /// 32 | /// Gets a message that describes the current exception. 33 | /// 34 | public override string Message 35 | { 36 | get { return GetErrorMessage(); } 37 | } 38 | 39 | /// 40 | /// Gets a collection of occured errors. 41 | /// 42 | public IEnumerable Errors 43 | { 44 | get { return _errors.Cast(); } 45 | } 46 | 47 | /// 48 | /// Gets a generated source code. 49 | /// 50 | public string SourceCode 51 | { 52 | get { return _sourceCode; } 53 | } 54 | 55 | /// 56 | /// Gets a template code. 57 | /// 58 | public string Template 59 | { 60 | get { return _template; } 61 | } 62 | 63 | private string GetErrorMessage() 64 | { 65 | var result = new StringBuilder(); 66 | result.AppendLine("An errors was occured on template compilation:"); 67 | result.AppendLine(); 68 | 69 | foreach (var error in Errors) 70 | { 71 | result.AppendLine(error.ToString()); 72 | result.AppendLine(); 73 | } 74 | 75 | result.AppendLine("Template source code:"); 76 | result.AppendLine(Template); 77 | result.AppendLine(); 78 | 79 | result.AppendLine("Generated source code:"); 80 | if (string.IsNullOrEmpty(SourceCode)) 81 | { 82 | result.AppendLine("Generated source code is not available."); 83 | result.AppendLine("For enable source code turn on Template.Debug option."); 84 | } 85 | else 86 | { 87 | result.AppendLine(SourceCode); 88 | } 89 | 90 | return result.ToString(); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/RazorTemplates.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 | 2 | False 3 | True 4 | ItShouldUseTheTypedModel 5 | <Session><Elements><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.Bugs.AddingExtraPeriodToSomeUrls" type="MsTestTestClassElement" TypeName="RazorTemplates.Tests.Bugs.AddingExtraPeriodToSomeUrls" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.TemplateTests" type="MsTestTestClassElement" TypeName="RazorTemplates.Tests.TemplateTests" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.Bugs.UsingLinqInTemlate" type="MsTestTestClassElement" TypeName="RazorTemplates.Tests.Bugs.UsingLinqInTemlate" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.Bugs.UsingLinqInTemlate.ItShouldAllowToUseLinqInTemplates" ParentId="MsTest:RazorTemplates.Tests.Bugs.UsingLinqInTemlate" type="MsTestTestMethodElement" MethodName="ItShouldAllowToUseLinqInTemplates" TypeName="RazorTemplates.Tests.Bugs.UsingLinqInTemlate" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.Bugs.AddingExtraPeriodToSomeUrls.ItShouldNotAddExtraPeriodToUrls" ParentId="MsTest:RazorTemplates.Tests.Bugs.AddingExtraPeriodToSomeUrls" type="MsTestTestMethodElement" MethodName="ItShouldNotAddExtraPeriodToUrls" TypeName="RazorTemplates.Tests.Bugs.AddingExtraPeriodToSomeUrls" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.TemplateTests.ItShouldRenderExpandoObjects" ParentId="MsTest:RazorTemplates.Tests.TemplateTests" type="MsTestTestMethodElement" MethodName="ItShouldRenderExpandoObjects" TypeName="RazorTemplates.Tests.TemplateTests" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.TemplateTests.ItShouldSupportAnnonymousObjects" ParentId="MsTest:RazorTemplates.Tests.TemplateTests" type="MsTestTestMethodElement" MethodName="ItShouldSupportAnnonymousObjects" TypeName="RazorTemplates.Tests.TemplateTests" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /><UnitTestElement Provider="MSTest" Id="MsTest:RazorTemplates.Tests.TemplateTests.ItShouldUseTheTypedModel" ParentId="MsTest:RazorTemplates.Tests.TemplateTests" type="MsTestTestMethodElement" MethodName="ItShouldUseTheTypedModel" TypeName="RazorTemplates.Tests.TemplateTests" Project="1D575AD0-6C38-466A-ADB2-E550AFB94E43" /></Elements></Session> -------------------------------------------------------------------------------- /src/RazorTemplates.Core/RazorTemplates.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F4411488-D1A2-4A06-8891-E44C3BDCC4A8} 8 | Library 9 | Properties 10 | RazorTemplates.Core 11 | RazorTemplates.Core 12 | v4.0 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | bin\Debug\RazorTemplates.Core.XML 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | bin\Release\RazorTemplates.Core.XML 33 | 34 | 35 | true 36 | 37 | 38 | RazorTemplates.snk 39 | 40 | 41 | 42 | 43 | 44 | 45 | True 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateDescription`2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace RazorTemplates.Core 5 | { 6 | /// 7 | /// Represents a description of new Razor template type. 8 | /// 9 | public class TemplateDescription where T : TemplateBase 10 | { 11 | private readonly HashSet _namespaces = new HashSet(); 12 | private readonly HashSet _assemblyFileNames = new HashSet(); 13 | private readonly Action _templateInitializer; 14 | 15 | /// 16 | /// Initializes a new instance of TemplateDescription class. 17 | /// 18 | internal TemplateDescription(Action templateInitializer) 19 | { 20 | _templateInitializer = templateInitializer; 21 | } 22 | 23 | /// 24 | /// Gets a directory which Razor compiler should use to store temp files 25 | /// 26 | internal string CompilationDirectory { get; set; } 27 | 28 | /// 29 | /// Specifies that Razor template should uses specified namespace. 30 | /// 31 | public TemplateDescription AddNamespace(string @namespace) 32 | { 33 | _namespaces.Add(@namespace); 34 | return this; 35 | } 36 | 37 | /// 38 | /// Specifies that Razor template should load specified assemblies 39 | /// 40 | public TemplateDescription AddAssemblies(params string[] assemblyFileNames) 41 | { 42 | foreach (var assemblyFileName in assemblyFileNames) 43 | { 44 | _assemblyFileNames.Add(assemblyFileName); 45 | } 46 | return this; 47 | } 48 | 49 | /// 50 | /// Specifies that Razor compiler should use specified directory to store temp files. 51 | /// 52 | public TemplateDescription CompileTo(string compilationDirectory) 53 | { 54 | if (string.IsNullOrWhiteSpace(compilationDirectory)) 55 | throw new ArgumentException( 56 | "Compilation directory can't be null or whitespace.", 57 | "compilationDirectory"); 58 | 59 | CompilationDirectory = compilationDirectory; 60 | return this; 61 | } 62 | 63 | /// 64 | /// Creates template from specified source. 65 | /// 66 | public ITemplate Compile(string source) 67 | { 68 | var compilationResult = InternalCompile(source); 69 | return new Template(compilationResult.Type, compilationResult.SourceCode, _templateInitializer); 70 | } 71 | 72 | /// 73 | /// Creates template from specified source. 74 | /// 75 | public ITemplate Compile(string source) 76 | { 77 | if (string.IsNullOrEmpty(source)) 78 | throw new ArgumentException( 79 | "Template source can't be null or empty string.", 80 | "source"); 81 | 82 | Action> initializer = null; 83 | if (_templateInitializer != null) 84 | { 85 | initializer = _templateInitializer as Action>; 86 | if (initializer == null) 87 | throw new InvalidOperationException( 88 | string.Format( 89 | "Template initializer should be a '{0}', but '{1}' accepted.", 90 | typeof(Action>), 91 | _templateInitializer.GetType())); 92 | } 93 | 94 | var compilationResult = InternalCompile(source); 95 | return new Template, TModel>(compilationResult.Type, compilationResult.SourceCode, initializer); 96 | } 97 | 98 | internal TemplateCompilationResult InternalCompile(string source) 99 | { 100 | return TemplateCompiler.Compile( 101 | typeof(T), 102 | source, 103 | _assemblyFileNames, 104 | _namespaces, 105 | CompilationDirectory); 106 | } 107 | 108 | internal TemplateCompilationResult InternalCompile(string source) 109 | { 110 | return TemplateCompiler.Compile( 111 | typeof(TemplateBase), 112 | source, 113 | _assemblyFileNames, 114 | _namespaces, 115 | CompilationDirectory); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /src/RazorTemplates.Tests/RazorTemplates.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {1D575AD0-6C38-466A-ADB2-E550AFB94E43} 7 | Library 8 | Properties 9 | RazorTemplates.Tests 10 | RazorTemplates.Tests 11 | v4.0 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | ..\packages\Newtonsoft.Json.6.0.1\lib\net40\Newtonsoft.Json.dll 41 | True 42 | 43 | 44 | 45 | 3.5 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {f4411488-d1a2-4a06-8891-e44c3bdcc4a8} 70 | RazorTemplates.Core 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | False 84 | 85 | 86 | False 87 | 88 | 89 | False 90 | 91 | 92 | False 93 | 94 | 95 | 96 | 97 | 98 | 99 | 106 | -------------------------------------------------------------------------------- /src/RazorTemplates.Core/TemplateCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom; 3 | using System.CodeDom.Compiler; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Web.Razor; 12 | using Microsoft.CSharp; 13 | using Microsoft.VisualBasic; 14 | 15 | namespace RazorTemplates.Core 16 | { 17 | internal static class TemplateCompiler 18 | { 19 | private const string TEMPLATES_NAMESPACE = "RazorTemplates"; 20 | 21 | private static readonly string[] _defaultNamespaces = new[] 22 | { 23 | "System", 24 | "System.Collections.Generic", 25 | "System.Linq" 26 | }; 27 | 28 | private static volatile bool _runtimeBinderLoaded; 29 | private static int _templateNumber; 30 | 31 | public static bool Debug { get; set; } 32 | 33 | public static TemplateCompilationLanguage Language { get; set; } 34 | 35 | internal static TemplateCompilationResult Compile( 36 | Type templateType, 37 | string templateBody, 38 | IEnumerable assemblyFileNames, 39 | IEnumerable namespaces, 40 | string tempDirectory) 41 | { 42 | LoadRuntimeBinder(); 43 | 44 | string className; 45 | var compileUnit = GetCodeCompileUnit(templateType, namespaces, templateBody, out className); 46 | 47 | string sourceCode; 48 | CodeDomProvider codeProvider; 49 | switch (Language) 50 | { 51 | case TemplateCompilationLanguage.CSharp: 52 | codeProvider = new CSharpCodeProvider(); 53 | break; 54 | case TemplateCompilationLanguage.VisualBasic: 55 | codeProvider = new VBCodeProvider(); 56 | break; 57 | default: 58 | throw new NotSupportedException("Language not supported."); 59 | } 60 | var builder = new StringBuilder(); 61 | 62 | using (var writer = new StringWriter(builder, CultureInfo.InvariantCulture)) 63 | { 64 | codeProvider.GenerateCodeFromCompileUnit(compileUnit, writer, new CodeGeneratorOptions()); 65 | sourceCode = builder.ToString(); 66 | } 67 | 68 | var parameters = CreateCompilerParameters(tempDirectory, assemblyFileNames); 69 | var compileResult = codeProvider.CompileAssemblyFromDom(parameters, compileUnit); 70 | if (compileResult.Errors != null && compileResult.Errors.Count > 0) 71 | throw new TemplateCompilationException(compileResult.Errors, sourceCode, templateBody); 72 | 73 | var fullClassName = TEMPLATES_NAMESPACE + "." + className; 74 | 75 | return new TemplateCompilationResult 76 | { 77 | Type = compileResult.CompiledAssembly.GetType(fullClassName), 78 | SourceCode = sourceCode 79 | }; 80 | } 81 | 82 | private static CompilerParameters CreateCompilerParameters(string tempDirectory, IEnumerable assemblyFileNames) 83 | { 84 | LoadAssemblies(assemblyFileNames); 85 | 86 | var parameters = new CompilerParameters 87 | { 88 | GenerateInMemory = true, 89 | GenerateExecutable = false, 90 | IncludeDebugInformation = false, 91 | CompilerOptions = "/target:library /optimize", 92 | }; 93 | 94 | if (Language == TemplateCompilationLanguage.VisualBasic) 95 | { 96 | parameters.CompilerOptions += " /optioninfer /optioncompare:text /optionstrict /optionexplicit"; 97 | } 98 | 99 | tempDirectory = string.IsNullOrWhiteSpace(tempDirectory) 100 | ? GetTempDirectoryFromEnvironment() 101 | : tempDirectory; 102 | 103 | if (!string.IsNullOrWhiteSpace(tempDirectory)) 104 | { 105 | tempDirectory = Path.Combine(tempDirectory, Guid.NewGuid().ToString("N")); 106 | if (!Directory.Exists(tempDirectory)) Directory.CreateDirectory(tempDirectory); 107 | 108 | parameters.TempFiles = new TempFileCollection(tempDirectory, false); 109 | } 110 | 111 | parameters.ReferencedAssemblies.AddRange(GetLoadedAssemblies()); 112 | 113 | return parameters; 114 | } 115 | 116 | private static string GetTempDirectoryFromEnvironment() 117 | { 118 | var tempDirectory = Environment.GetEnvironmentVariable("TEMP"); 119 | return !string.IsNullOrEmpty(tempDirectory) ? tempDirectory : Path.GetTempPath(); 120 | } 121 | 122 | private static void LoadAssemblies(IEnumerable assemblyFileNames) 123 | { 124 | if (assemblyFileNames == null) 125 | { 126 | return; 127 | } 128 | 129 | foreach (var assemblyFileName in assemblyFileNames) 130 | { 131 | var assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyFileName); 132 | var assembly = Assembly.LoadFile(assemblyPath); 133 | AppDomain.CurrentDomain.Load(assembly.GetName()); 134 | } 135 | } 136 | 137 | private static string[] GetLoadedAssemblies() 138 | { 139 | return AppDomain.CurrentDomain 140 | .GetAssemblies() 141 | .Where(a => !a.IsDynamic) 142 | .GroupBy(a => a.FullName) 143 | .Select(grp => grp.First()) 144 | .Select(a => a.Location) 145 | .Where(a => !String.IsNullOrWhiteSpace(a)) 146 | .ToArray(); 147 | } 148 | 149 | private static CodeCompileUnit GetCodeCompileUnit(Type templateType, IEnumerable namespaces, string templateBody, out string className) 150 | { 151 | var engine = CreateRazorEngine(templateType, namespaces, out className); 152 | 153 | GeneratorResults results; 154 | using (var textReader = new StringReader(templateBody)) 155 | results = engine.GenerateCode(textReader); 156 | 157 | return results.GeneratedCode; 158 | } 159 | 160 | private static RazorTemplateEngine CreateRazorEngine(Type templateType, IEnumerable namespaces, out string className) 161 | { 162 | RazorEngineHost host; 163 | switch (Language) 164 | { 165 | case TemplateCompilationLanguage.CSharp: 166 | host = new RazorEngineHost(new CSharpRazorCodeLanguage()); 167 | break; 168 | case TemplateCompilationLanguage.VisualBasic: 169 | host = new RazorEngineHost(new VBRazorCodeLanguage()); 170 | break; 171 | default: 172 | throw new NotSupportedException("Language not supported."); 173 | } 174 | 175 | className = "Template_" + GetNextTemplateNumber(); 176 | 177 | host.DefaultBaseClass = templateType.FullName; 178 | host.DefaultNamespace = TEMPLATES_NAMESPACE; 179 | host.DefaultClassName = className; 180 | 181 | foreach (var ns in _defaultNamespaces.Union(namespaces).Distinct()) 182 | host.NamespaceImports.Add(ns); 183 | 184 | return new RazorTemplateEngine(host); 185 | } 186 | 187 | private static string GetNextTemplateNumber() 188 | { 189 | var number = Interlocked.Increment(ref _templateNumber); 190 | return number.ToString(CultureInfo.InvariantCulture); 191 | } 192 | 193 | private static void LoadRuntimeBinder() 194 | { 195 | if (_runtimeBinderLoaded) return; 196 | 197 | var binderType = typeof(Microsoft.CSharp.RuntimeBinder.Binder); 198 | var binderAssemblyName = binderType.Assembly.FullName; 199 | if (string.IsNullOrEmpty(binderAssemblyName)) 200 | throw new InvalidOperationException( 201 | "Could not load 'Microsoft.CSharp.RuntimeBinder.Binder' assembly."); 202 | 203 | _runtimeBinderLoaded = true; 204 | } 205 | } 206 | } --------------------------------------------------------------------------------