v7.3.2018.0614   重构高性能资源池,减少GC压力,增加线程池,让异步任务得到平等竞争CPU的机会
大石头 编写于 2018-06-14 17:56:44
X
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.WebPages;
using NewLife.Reflection;

namespace NewLife.Cube.Precompiled
{
    /// <summary>预编译Mvc引擎</summary>
    public class PrecompiledMvcEngine : BuildManagerViewEngine, IVirtualPathFactory
    {
        private readonly IDictionary<String, Type> _mappings;
        private readonly String _baseVirtualPath;
        private readonly Lazy<DateTime> _assemblyLastWriteTime;
        //private readonly IViewPageActivator _viewPageActivator;

        /// <summary>取代物理文件,优先内嵌类</summary>
        public Boolean PreemptPhysicalFiles { get; set; }

        /// <summary>使用更新的物理文件</summary>
        public Boolean UsePhysicalViewsIfNewer { get; set; }

        /// <summary>实例化预编译Mvc引擎</summary>
        /// <param name="assembly"></param>
        public PrecompiledMvcEngine(Assembly assembly) : this(assembly, null) { }

        /// <summary>实例化预编译Mvc引擎</summary>
        /// <param name="assembly"></param>
        /// <param name="baseVirtualPath"></param>
        public PrecompiledMvcEngine(Assembly assembly, String baseVirtualPath) : this(assembly, baseVirtualPath, null) { }

        /// <summary>实例化预编译Mvc引擎</summary>
        /// <param name="assembly"></param>
        /// <param name="baseVirtualPath"></param>
        /// <param name="viewPageActivator"></param>
        public PrecompiledMvcEngine(Assembly assembly, String baseVirtualPath, IViewPageActivator viewPageActivator)
            : base(viewPageActivator)
        {
            // 为了实现物理文件“重载覆盖”的效果,强制使用物理文件
            PreemptPhysicalFiles = false;
            UsePhysicalViewsIfNewer = false;

            _assemblyLastWriteTime = new Lazy<DateTime>(() => assembly.GetLastWriteTimeUtc(DateTime.MaxValue));
            _baseVirtualPath = NormalizeBaseVirtualPath(baseVirtualPath);
            AreaViewLocationFormats = new String[]
            {
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml"
            };
            AreaMasterLocationFormats = new String[]
            {
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml"
            };
            AreaPartialViewLocationFormats = new String[]
            {
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml"
            };
            ViewLocationFormats = new String[]
            {
                "~/Views/{1}/{0}.cshtml",
                "~/Views/Shared/{0}.cshtml"
            };
            MasterLocationFormats = new String[]
            {
                "~/Views/{1}/{0}.cshtml",
                "~/Views/Shared/{0}.cshtml"
            };
            PartialViewLocationFormats = new String[]
            {
                "~/Views/{1}/{0}.cshtml",
                "~/Views/Shared/{0}.cshtml"
            };
            FileExtensions = new String[]
            {
                "cshtml"
            };
            _mappings = GetTypeMappings(assembly, _baseVirtualPath);
            ViewLocationCache = new PrecompiledViewLocationCache(assembly.FullName, ViewLocationCache);
            //_viewPageActivator = (viewPageActivator ?? (DependencyResolver.Current.GetService<IViewPageActivator>() ?? DefaultViewPageActivator.Current));
        }

        /// <summary>文件是否存在。如果存在,则由当前引擎创建视图</summary>
        /// <param name="controllerContext"></param>
        /// <param name="virtualPath"></param>
        /// <returns></returns>
        protected override Boolean FileExists(ControllerContext controllerContext, String virtualPath)
        {
            virtualPath = EnsureVirtualPathPrefix(virtualPath);

            // 如果映射表不存在,就不要掺合啦
            if (!Exists(virtualPath)) return false;

            // 两个条件任意一个满足即可使用物理文件
            // 如果不要求取代物理文件,并且虚拟文件存在,则使用物理文件创建
            if (!PreemptPhysicalFiles && VirtualPathProvider.FileExists(virtualPath)) return false;

            // 如果使用较新的物理文件,且物理文件的确较新,则使用物理文件创建
            if (UsePhysicalViewsIfNewer && IsPhysicalFileNewer(virtualPath)) return false;

            return true;
        }

        /// <summary>创建分部视图</summary>
        /// <param name="controllerContext"></param>
        /// <param name="partialPath"></param>
        /// <returns></returns>
        protected override IView CreatePartialView(ControllerContext controllerContext, String partialPath)
        {
            partialPath = EnsureVirtualPathPrefix(partialPath);
            return CreateViewInternal(partialPath, null, false);
        }

        /// <summary>创建视图</summary>
        /// <param name="controllerContext"></param>
        /// <param name="viewPath"></param>
        /// <param name="masterPath"></param>
        /// <returns></returns>
        protected override IView CreateView(ControllerContext controllerContext, String viewPath, String masterPath)
        {
            viewPath = EnsureVirtualPathPrefix(viewPath);
            return CreateViewInternal(viewPath, masterPath, true);
        }

        private IView CreateViewInternal(String viewPath, String masterPath, Boolean runViewStartPages)
        {
            Type type;
            if (!_mappings.TryGetValue(viewPath, out type)) return null;

            return new PrecompiledMvcView(viewPath, masterPath, type, runViewStartPages, FileExtensions, ViewPageActivator);
        }

        /// <summary>创建实例。Start和Layout会调用这里</summary>
        /// <param name="virtualPath"></param>
        /// <returns></returns>
        public Object CreateInstance(String virtualPath)
        {
            virtualPath = EnsureVirtualPathPrefix(virtualPath);

            // 两个条件任意一个满足即可使用物理文件
            // 如果不要求取代物理文件,并且虚拟文件存在,则使用物理文件创建
            if (!PreemptPhysicalFiles && VirtualPathProvider.FileExists(virtualPath))
                return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(WebPageRenderingBase));

            // 如果使用较新的物理文件,且物理文件的确较新,则使用物理文件创建
            if (UsePhysicalViewsIfNewer && IsPhysicalFileNewer(virtualPath))
                return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(WebViewPage));

            // 最后使用内嵌类创建
            Type type;
            if (_mappings.TryGetValue(virtualPath, out type))
                return ViewPageActivator.Create(null, type);

            return null;
        }

        /// <summary>是否存在</summary>
        /// <param name="virtualPath"></param>
        /// <returns></returns>
        public Boolean Exists(String virtualPath)
        {
            virtualPath = EnsureVirtualPathPrefix(virtualPath);
            return _mappings.ContainsKey(virtualPath);
        }

        private Boolean IsPhysicalFileNewer(String virtualPath)
        {
            return IsPhysicalFileNewer(virtualPath, _baseVirtualPath, _assemblyLastWriteTime);
        }

        /// <summary>是否物理文件更新</summary>
        /// <param name="virtualPath"></param>
        /// <param name="baseVirtualPath"></param>
        /// <param name="assemblyLastWriteTime"></param>
        /// <returns></returns>
        internal static Boolean IsPhysicalFileNewer(String virtualPath, String baseVirtualPath, Lazy<DateTime> assemblyLastWriteTime)
        {
            if (!virtualPath.StartsWithIgnoreCase(baseVirtualPath + "")) return false;

            if (!String.IsNullOrEmpty(baseVirtualPath)) virtualPath = "~/" + virtualPath.Substring(baseVirtualPath.Length);

            var path = HostingEnvironment.MapPath(virtualPath);
            return File.Exists(path) && File.GetLastWriteTimeUtc(path) > assemblyLastWriteTime.Value;
        }

        private static String EnsureVirtualPathPrefix(String virtualPath)
        {
            if (!String.IsNullOrEmpty(virtualPath))
            {
                if (!virtualPath.StartsWith("~/", StringComparison.Ordinal))
                    virtualPath = "~/" + virtualPath.TrimStart('/', '~');
            }
            return virtualPath;
        }

        internal static String NormalizeBaseVirtualPath(String virtualPath)
        {
            if (!String.IsNullOrEmpty(virtualPath))
                virtualPath = EnsureVirtualPathPrefix(virtualPath).EnsureEnd("/");

            return virtualPath;
        }

        private static String CombineVirtualPaths(String baseVirtualPath, String virtualPath)
        {
            if (!String.IsNullOrEmpty(baseVirtualPath))
                return VirtualPathUtility.Combine(baseVirtualPath, virtualPath.Substring(2));
            else
                return virtualPath;
        }

        /// <summary>遍历获取所有类型映射</summary>
        /// <param name="asm"></param>
        /// <param name="baseVirtualPath"></param>
        /// <returns></returns>
        public static IDictionary<String, Type> GetTypeMappings(Assembly asm, String baseVirtualPath)
        {
            return (
                from type in asm.GetTypes()
                where type.As<WebPageRenderingBase>()
                let pageVirtualPath = type.GetCustomAttributes(false).OfType<PageVirtualPathAttribute>().FirstOrDefault()
                where pageVirtualPath != null
                select new KeyValuePair<String, Type>(CombineVirtualPaths(baseVirtualPath, pageVirtualPath.VirtualPath), type)).ToDictionary(t => t.Key, t => t.Value, StringComparer.OrdinalIgnoreCase);
        }
    }
}