Jx.Cms开发笔记(六)-重写Compiler

博客 分享
0 228
张三
张三 2022-04-20 22:58:15
悬赏:0 积分 收藏

Jx.Cms开发笔记(六)-重写Compiler

我们在Jx.Cms开发笔记(三)-Views主题动态切换中说了如何切换主题。但是这里有一个问题,就是主题切换时,会报错

theme.png

这是由于asp.net core在处理Views的信息的时候是在构造函数中处理的,没有任何方法可以刷新这个处理结果。

这里放最新版的DefaultViewCompiler代码,在Jx.Cms编写的时候代码有少许区别,但是基本逻辑是一样的。

public DefaultViewCompiler(        ApplicationPartManager applicationPartManager,        ILogger<DefaultViewCompiler> logger)    {        _applicationPartManager = applicationPartManager;        _logger = logger;        _normalizedPathCache = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);        EnsureCompiledViews(logger);    }    [MemberNotNull(nameof(_compiledViews))]    private void EnsureCompiledViews(ILogger logger)    {        if (_compiledViews is not null)        {            return;        }        var viewsFeature = new ViewsFeature();        _applicationPartManager.PopulateFeature(viewsFeature);        // We need to validate that the all compiled views are unique by path (case-insensitive).        // We do this because there's no good way to canonicalize paths on windows, and it will create        // problems when deploying to linux. Rather than deal with these issues, we just don't support        // views that differ only by case.        var compiledViews = new Dictionary<string, Task<CompiledViewDescriptor>>(            viewsFeature.ViewDescriptors.Count,            StringComparer.OrdinalIgnoreCase);        foreach (var compiledView in viewsFeature.ViewDescriptors)        {            logger.ViewCompilerLocatedCompiledView(compiledView.RelativePath);            if (!compiledViews.ContainsKey(compiledView.RelativePath))            {                // View ordering has precedence semantics, a view with a higher precedence was not                // already added to the list.                compiledViews.TryAdd(compiledView.RelativePath, Task.FromResult(compiledView));            }        }        if (compiledViews.Count == 0)        {            logger.ViewCompilerNoCompiledViewsFound();        }        // Safe races should be ok. We would end up logging multiple times        // if this is invoked concurrently, but since this is primarily a dev-scenario, we don't think        // this will happen often. We could always re-consider the logging if we get feedback.        _compiledViews = compiledViews;    }

所以程序只能获取到第一次的_compiledViews,切换后的Views由于没有放在_compiledViews中,所以无法被找到,就出现了第一图的那种错误。

这里的解决方法很简单,我们只需要重写一个自己的ViewCompiler就可以了,由于官方源码全部都是internal的,所以我们只能把这部分内容全部重写。

我们创建自己的MyViewCompilerProvider和MyViewCompiler。由于.Net6在这里有部分修改,我们的程序是在.Net5时编写的,所以这里我们的源码是.Net5修改的,与目前最新的代码有些许差距,但是不影响正常使用.

MyViewCompiler:

public class MyViewCompiler : IViewCompiler    {        private readonly Dictionary<string, Task<CompiledViewDescriptor>> _compiledViews;        private readonly ConcurrentDictionary<string, string> _normalizedPathCache;        private readonly ILogger _logger;        public MyViewCompiler(            IList<CompiledViewDescriptor> compiledViews,            ILogger logger)        {            if (compiledViews == null)            {                throw new ArgumentNullException(nameof(compiledViews));            }            if (logger == null)            {                throw new ArgumentNullException(nameof(logger));            }            _logger = logger;            _normalizedPathCache = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);            // We need to validate that the all of the precompiled views are unique by path (case-insensitive).            // We do this because there's no good way to canonicalize paths on windows, and it will create            // problems when deploying to linux. Rather than deal with these issues, we just don't support            // views that differ only by case.            _compiledViews = new Dictionary<string, Task<CompiledViewDescriptor>>(                compiledViews.Count,                StringComparer.OrdinalIgnoreCase);            foreach (var compiledView in compiledViews)            {                if (!_compiledViews.ContainsKey(compiledView.RelativePath))                {                    // View ordering has precedence semantics, a view with a higher precedence was not                    // already added to the list.                    _compiledViews.Add(compiledView.RelativePath, Task.FromResult(compiledView));                }            }            if (_compiledViews.Count == 0)            {                            }        }        /// <inheritdoc />        public Task<CompiledViewDescriptor> CompileAsync(string relativePath)        {            if (relativePath == null)            {                throw new ArgumentNullException(nameof(relativePath));            }            // Attempt to lookup the cache entry using the passed in path. This will succeed if the path is already            // normalized and a cache entry exists.            if (_compiledViews.TryGetValue(relativePath, out var cachedResult))            {                                return cachedResult;            }            var normalizedPath = GetNormalizedPath(relativePath);            if (_compiledViews.TryGetValue(normalizedPath, out cachedResult))            {                                return cachedResult;            }            // Entry does not exist. Attempt to create one.                        return Task.FromResult(new CompiledViewDescriptor            {                RelativePath = normalizedPath,                ExpirationTokens = Array.Empty<IChangeToken>(),            });        }        private string GetNormalizedPath(string relativePath)        {            Debug.Assert(relativePath != null);            if (relativePath.Length == 0)            {                return relativePath;            }            if (!_normalizedPathCache.TryGetValue(relativePath, out var normalizedPath))            {                normalizedPath = NormalizePath(relativePath);                _normalizedPathCache[relativePath] = normalizedPath;            }            return normalizedPath;        }        public static string NormalizePath(string path)        {            var addLeadingSlash = path[0] != '\\' && path[0] != '/';            var transformSlashes = path.IndexOf('\\') != -1;            if (!addLeadingSlash && !transformSlashes)            {                return path;            }            var length = path.Length;            if (addLeadingSlash)            {                length++;            }            return string.Create(length, (path, addLeadingSlash), (span, tuple) =>            {                var (pathValue, addLeadingSlashValue) = tuple;                var spanIndex = 0;                if (addLeadingSlashValue)                {                    span[spanIndex++] = '/';                }                foreach (var ch in pathValue)                {                    span[spanIndex++] = ch == '\\' ? '/' : ch;                }            });        }    }

这个类完全复制了.Net5的源码,只是删除了部分编译不过去的日志内容。

MyViewCompilerProvider:

public class MyViewCompilerProvider : IViewCompilerProvider    {        private MyViewCompiler _compiler;        private readonly ApplicationPartManager _applicationPartManager;        private readonly ILoggerFactory _loggerFactory;        public MyViewCompilerProvider(            ApplicationPartManager applicationPartManager,            ILoggerFactory loggerFactory)        {            _applicationPartManager = applicationPartManager;            _loggerFactory = loggerFactory;            Modify();        }        public void Modify()        {            var feature = new ViewsFeature();            _applicationPartManager.PopulateFeature(feature);            _compiler = new MyViewCompiler(feature.ViewDescriptors, _loggerFactory.CreateLogger<MyViewCompiler>());        }        public IViewCompiler GetCompiler() => _compiler;    }

这个类我们只是把.Net5源码里的构造函数拆分了,拆出了一个Public的Modify方法。

然后我们需要用自己的MyViewCompilerProvider替换自带的,所以我们需要在Startup.cs的ConfigureServices方法中添加services.Replace<IViewCompilerProvider, MyViewCompilerProvider>();

最后我们只需要在需要重新获取所有Views的地方调用viewCompilerProvider?.Modify();即可。

posted @ 2022-04-20 21:58 jvx 阅读(11) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员