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

这是由于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();即可。
Jx.Cms开发笔记(六)-重写Compiler的更多相关文章
- Jx.Cms开发笔记(一)-Jx.Cms介绍
开始 从今天开始,我们将开启Jx.Cms系列开发教程. 我们将会使用Jx.Cms来介绍Blazor的开发.MVC的开发,热插拔插件的开发等等一系列开发教程. 介绍 Jx.Cms是一个使用最新版.NET ...
- Jx.Cms开发笔记(二)-系统登录
界面 此界面完全抄了BootstrapAdmin css隔离 由于登录页面的css与其他页面没有什么关系,所以为了防止其他界面的css被污染,我们需要使用css隔离. css隔离需要在_Host.cs ...
- Jx.Cms开发笔记(四)-改造Card组件
在Blazor 组件库 BootstrapBlazor 中Card组件介绍中我们说过,如果我们使用了Card组件的IsCollapsible属性设置了可伸缩的话,就只能使用Text属性来设置标题文本, ...
- Jx.Cms开发笔记(五)-文章编辑页面标签设计
标签页的样子 设计思路 与其他输入框一样,存在一个Label标签,由于这里不像其他输入框一样可以直接使用Row标签,所以这里需要额外增加. 使用Tag组件显示所有的标签,我们在Blazor 组件库 B ...
- Django开发笔记六
Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.登录功能完善 登录成功应该是重定向到首页,而不是转发 ...
- 钉钉开发笔记(六)使用Google浏览器做真机页面调试
注: 参考文献:https://developers.google.com/web/ 部分字段为翻译文献,水平有限,如有错误敬请指正 步骤1: 从Windows,Mac或Linux计算机远程调试And ...
- 【django】CMS开发笔记一:虚拟环境配置
项目代码:https://github.com/pusidun/CMS-django 使用虚拟环境 虚拟环境是Python解释器的虚拟副本.在虚拟环境中安装私有包,不会影响全局的Python解释器.可 ...
- cms开发笔记2
1 创建数据库表 //配置文件CREATE TABLE IF NOT EXISTS `mc_config` ( `en_name` varchar(80) NOT NULL, `ch_name` va ...
- Django开发笔记三
Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.基于类的方式重写登录:views.py: from ...
随机推荐
- 使用 Mosh 来优化 SSH 连接
1.什么是Mosh Mosh表示移动Shell(Mobile Shell),是一个用于从客户端跨互联网连接远程服务器的命令行工具.它能用于SSH连接,但是比Secure Shell功能更多.它是一个类 ...
- Aspect 切面?
AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能.比如,一个日志模块可以被称作日志的AOP切面.根据需求的不同,一个应用程序可以有若干切面.在Spring ...
- MariaDB CAST语法
Syntax CAST(expr AS type) Description CAST()函数采用一种类型的值,并产生另一种类型的值,类似于CONVERT函数. CAST()和CONVERT()之间的主 ...
- Java语言的特点有哪些?
1.简单 Java最初是为对家用电器进行集成控制而设计的一种语言,因此它必须简单明了.Java语言的简单性主要体现在以下三个方面: 1) Java的风格类似于C++,因而C++程序员是非常熟悉的.从某 ...
- ArrayList 与 LinkedList 的不区别?
最明显的区别是 ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构书链表,不支持随机访问.使用下标访问一个元素, ArrayList 的时间复杂度是 O ...
- SQL函数对应的数据库(案例)
- 【动态规划】洛谷P1802 5 倍经验日(01背包问题)
一个洛谷普及-的题目,也是我刚刚入门学习动态规划的练习题. 下面发一下我的思路和代码题解: 我的思路及伪代码: 我的AC图: 接下来上代码: 1 //动态规划 洛谷P1802 五倍经验日 2 #inc ...
- python学习笔记(二)——程序结构
1. 选择结构: if 语句:单分支.双分支.多分支 **单分支结构** if 条件表达式: 语句块 **双分支结构** if 条件表达式: 语句块 else: 语句块 **多分支结构** if 条件 ...
- 一次关于关系抽取(RE)综述调研的交流心得
本文来自于一次交流的的记录,{}内的为个人体会. 基本概念 实事知识:实体-关系-实体的三元组.比如, 知识图谱:大量实时知识组织在一起,可以构建成知识图谱. 关系抽取:由于文本中蕴含大量事实知识,需 ...
- Vuet.js规则详解,它是你不知道的强大功能?
Vuet.js是什么? Vuet.js是给Vue.js提供状态管理的一个工具,与vuex不同,它是一种崇尚规则定制的状态管理模式.事先将状态更新的规则写好,然后将规则注入到组件中,然后状态按照预订的规 ...