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 ...
随机推荐
- case 函数语法与使用
case 函数是聚合函数的一种,为统计函数. case表达式: CASE selector WHEN value1 THEN action1; WHEN value2 THEN action2; WH ...
- 什么是GUI?
图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面.图形用户界面是一种人与计算机通信的界面显示格式,允许用户使用鼠 ...
- 如何通过sql语句完成分页?
oracle select rownum,bookId from [rownum是伪列名,bookId是列名] (select rownum row_id,bookId from xiaoWJ_boo ...
- Hystrix相关注解?
@EnableHystrix:开启熔断 @HystrixCommand(fallbackMethod="XXX"):声明一个失败回滚处理函数XXX,当被注解的方法执行超时(默认是 ...
- java使用正则表达式在文档里找匹配
public static void main(String[] args) { String str = "123我是456张三789的学生"; String regex2 = ...
- 遇到的问题之"项目启动加载不出页面"
已找到解决方案1 需要启动这两个微服务(注意:这个属于个人,你们也可以看看是否关联到相关微服务未启动)
- 前馈控制+PID
参考来源: 北京交通大学 硕士学位论文 基于脉冲串控制的含位置反馈和前馈补偿的位置控制算法的研究 赵旺升
- AD18布线技巧
3. 快乐打孔模式(颜色配置)PCB 设计完成后,补回流孔,需要打开多层,软件设置如下: 设置方法: 转载原文链接未知
- 安装Prettier
安装Prettier Prettier是优化代码格式的工具,可优化JavaScript.TypeScript.JSON等代码及配置文件. 使用命令yarn add -D --exact prettie ...
- js中一个函数调用另一个函数变量;判断鼠标按住/手指左右滑动
<script> function myFunction() { var x1=event.clientX;//只需要将被调用的变量前的var删除即可在下个函数调用: document.g ...