[1].[代码] 对Stream进行编译的 Compiler接口定义 跳至 [1] [2] [3] [4] [5] [6]

using System;
using System.IO;

namespace Skight.Arch.Presentation.Web.Core.ViewEngins.TemplateProvider
{
    public interface Compiler
    {
        Type compile_template(Stream stream);
        Type compile_template<T>(Stream stream);
    }
}

[2].[代码] 对磁盘文件编译的 FileCompiler接口定义 跳至 [1] [2] [3] [4] [5] [6]

using System;

namespace Skight.Arch.Presentation.Web.Core.ViewEngins.TemplateProvider
{
    public interface FileCompiler
    {
        Type compile_template<T>(string path);
        Type compile_template(string path);
    }
}

[3].[代码] 文件编译器的实现,内部使用Stream的Compiler.(组合模式) 跳至 [1] [2] [3] [4] [5] [6]

using System;
using System.IO;
using Skight.Arch.Domain.Containers;

namespace Skight.Arch.Presentation.Web.Core.ViewEngins.TemplateProvider
{
    [RegisterInContainer(LifeCycle.singleton)]
    public class FileCompilerImpl :FileCompiler
    {
        private Compiler internal_compiler;

        public  FileCompilerImpl(Compiler internalCompiler)
        {
            internal_compiler = internalCompiler;
        }

        protected FileCompilerImpl()
        {
        }

        public virtual  Type compile_template<T>(string path)
        {
            return internal_compiler.compile_template<T>(new FileStream(path, FileMode.Open, FileAccess.Read));
        }

        public virtual Type compile_template(string path) 
        {
           return internal_compiler.compile_template(new FileStream(path, FileMode.Open, FileAccess.Read));
        } 
    }
}

[4].[代码] 缓冲文件编译器,同样是对文件编译器的实现。可以通过Decorator模式,联合上面文件编译器使用 跳至 [1] [2] [3] [4] [5] [6]

using System;
using System.Collections.Concurrent;
using Skight.Arch.Domain.Containers;

namespace Skight.Arch.Presentation.Web.Core.ViewEngins.TemplateProvider
{
    [RegisterInContainer(LifeCycle.singleton)]
    public class CachedFileCompiler:FileCompiler
    {
        //cache of already compiled types
        ConcurrentDictionary<Tuple<string, Type>, Type> cache = new ConcurrentDictionary<Tuple<string, Type>, Type>();
        ConcurrentDictionary<string, Type> cache_for_no_model = new ConcurrentDictionary<string, Type>();

        private FileCompilerImpl internal_compiler;

        public CachedFileCompiler(FileCompilerImpl internalCompiler)
        {
            internal_compiler = internalCompiler;
        }

        public Type compile_template<T>(string path)
        {
            var key = Tuple.Create(path, typeof(T));
            Type type;

            if (!cache.TryGetValue(key, out type)) 
            {
                type =internal_compiler.compile_template<T>(path);
                cache[key] = type;
            }
            return type;
        }

        public Type compile_template(string path) 
        {
            Type type;
            if (!cache_for_no_model.TryGetValue(path, out type)) {
                type =internal_compiler.compile_template(path);
                cache_for_no_model[path] = type;
            }
           return type;
        }  
    }
}

[5].[代码] 模板生成器,依赖上面的文件编译器。也是,整个Razor编译器的入口。 跳至 [1] [2] [3] [4] [5] [6]

using System;
using System.Collections;
using Skight.Arch.Domain.Containers;

namespace Skight.Arch.Presentation.Web.Core.ViewEngins.TemplateProvider
{
    [RegisterInContainer(LifeCycle.singleton)]
    public class TemplateGenerator
    {
        private CachedFileCompiler internal_compiler;

        public TemplateGenerator(CachedFileCompiler internalCompiler)
        {
            internal_compiler = internalCompiler;
        }

        public TemplateBase<T> generate<T>(T model, IDictionary context, string path)
        {
            var type= internal_compiler.compile_template<T>(path);
            var instance = (TemplateBase<T>)Activator.CreateInstance(type);
            instance.Path = path;
            instance.Model = model;
            instance.Context = context;
            instance.Url = new UrlHelper(context);
            instance.Decoder = new DecodingHelper(context);
            return instance;
        }

        public TemplateBase generate(IDictionary context, string path) {
            var type = internal_compiler.compile_template(path);
            var instance = (TemplateBase)Activator.CreateInstance(type);
            instance.Path = path;
            instance.Context = context;
            instance.Url = new UrlHelper(context);
            instance.Decoder = new DecodingHelper(context);
            return instance;
        }
    }
}

[6].[代码] 最后一个, 真正的Razor Compiler。因为,大部分代码和原来的博客内容差不多,所以放在最后。 跳至 [1] [2] [3] [4] [5] [6]

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Linq;
using System.Web.Configuration;
using System.Web.Razor;
using System.Web.Razor.Generator;
using System.Web.Razor.Parser;
using Microsoft.CSharp;
using Skight.Arch.Domain.Containers;

namespace Skight.Arch.Presentation.Web.Core.ViewEngins.TemplateProvider.Razor
{
    [RegisterInContainer(LifeCycle.singleton)]
    public class RazorCompiler : Compiler
    {

        public Type compile_template(Stream stream)
        {
           return compile(stream, typeof (TemplateBase));
        }
        public Type compile_template<T>(Stream stream)
        {
            return compile(stream, typeof (TemplateBase<T>));
        }

        private Type compile(Stream stream,Type base_type)
        {
            var key = "c" + Guid.NewGuid().ToString("N");

            var parser = new HtmlMarkupParser();

            var host = new RazorEngineHost(new CSharpRazorCodeLanguage(), () => parser)
            {
                DefaultBaseClass = base_type.FullName,
                DefaultClassName = key,
                DefaultNamespace = "Skight.Arch.Presentation.Web.Core.ViewEngins.Razor.dynamic",
                GeneratedClassContext = new GeneratedClassContext("Execute", "Write", "WriteLiteral", "WriteTo", "WriteLiteralTo", "tinyweb.viewengine.razor.RazorCompiler.TemplateBase")
            };

            //always include this one

            host.NamespaceImports.Add("Skight.Arch.Presentation.Web.Core.ViewEngins");
            host.NamespaceImports.Add("System");

            //read web.config pages/namespaces
            if (File.Exists("\\web.config")) {
                var config = WebConfigurationManager.OpenWebConfiguration("\\web.config");
                var pages = config.GetSection("system.web/pages");
                if (pages != null) {
                    PagesSection pageSection = (PagesSection)pages;
                    for (int i = 0; i < pageSection.Namespaces.Count; i++) {
                        //this automatically ignores namespaces already added
                        host.NamespaceImports.Add(pageSection.Namespaces[i].Namespace);
                    }
                }
            }

            CodeCompileUnit code;
            using (var reader = new StreamReader(stream)) {
                var generatedCode = new RazorTemplateEngine(host).GenerateCode(reader);
                code = generatedCode.GeneratedCode;
            }

            var @params = new CompilerParameters
            {
                IncludeDebugInformation = false,
                TempFiles = new TempFileCollection(AppDomain.CurrentDomain.DynamicDirectory),
                CompilerOptions = "/target:library /optimize",
                GenerateInMemory = false
            };

            var assemblies = AppDomain.CurrentDomain
               .GetAssemblies()
               .Where(a => !a.IsDynamic)
               .Select(a => a.Location)
               .ToArray();

            @params.ReferencedAssemblies.AddRange(assemblies);

            var provider = new CSharpCodeProvider();
            var compiled = provider.CompileAssemblyFromDom(@params, code);

            if (compiled.Errors.Count > 0) {
                var compileErrors = string.Join("\r\n", compiled.Errors.Cast<object>().Select(o => o.ToString()));
                throw new ApplicationException("Failed to compile Razor:" + compileErrors);
            }

            return compiled.CompiledAssembly.GetType("Skight.Arch.Presentation.Web.Core.ViewEngins.Razor.dynamic." + key);
        }
    }
}