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 ...
随机推荐
- 有关电控制图软件EPLAN的安装,下面有破解版本2.7
前段时间刚刚接触这一块,就安装个软件老是出问题,所以我通过自己的努力学会啦,来给正要学习EPLAN的同学发福利啦 15:07:48 安装包发放在百度网盘来自取呀 建议安装我勾选的这个哦 链接:htt ...
- python3 爬虫5--分析Robots协议
1Robots协议 Robots协议告诉了搜索引擎和爬虫那些页面可以抓取,那些不可以,通常是存放在robots.txt文件里面,位于网站的根目录下 robots.txt中内容的示范: User-age ...
- Mysql之InnoDB行格式、数据页结构
Mysql架构图 存储引擎负责对表中的数据的进行读取和写入,常用的存储引擎有InnoDB.MyISAM.Memory等,不同的存储引擎有自己的特性,数据在不同存储引擎中存放的格式也是不同的,比如Mem ...
- java-字节流-字符流
I/O叙述 FileOutputStream类字节输出流的介绍: 写入数据的原理 java程序-->JVM(java虚拟机)--->OS(操作系统)---->OS调用写数据的方法-- ...
- volatile 能使得一个非原子操作变成原子操作吗?
一个典型的例子是在类中有一个 long 类型的成员变量.如果你知道该成员变量 会被多个线程访问,如计数器.价格等,你最好是将其设置为 volatile.为什么? 因为 Java 中读取 long 类型 ...
- 什么是 UML?
UML 是统一建模语言(Unified Modeling Language)的缩写,它发表于 1997 年,综合了当时已经存在的面向对象的建模语言.方法和过程,是一个支持模型 化和软件系统开发的图形化 ...
- 学习FastDfs(四)
1.简介 FastDFS 是一个开源的高性能分布式文件系统(DFS). 它的主要功能包括:文件存储,文件同步和文件访问,以及高容量和负载平衡.主要解决了海量数据存储问题,特别适合以中小文件(建议范围: ...
- php安装扩展的两种方法
方法一:使用yum命令安装 1.yum install libevent-devel 2.pecl install channel://pecl.php.net/libevent-0.1.0 3.ec ...
- 攻防世界 unserialize3
unserialize3 class xctf{ public $flag = '111'; public function __wakeup(){ exit('bad requests'); } } ...
- DevEco Device Tool 3.0 Release 新版本发布,支持多人共享开发
DevEco Device Tool 是面向智能设备开发者提供的一站式集成开发环境,支持 HarmonyOS Connect 的组件按需定制,支持代码编辑.编译.烧录和调试.性能监测等功能,支持 C/ ...