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 ...
随机推荐
- K8S原来如此简单(五)Metrics Server与HPA
什么是HPA https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/ 我们前面有通过kubectl ...
- Python中的鸭子类型
今天,我们来聊一聊Python中的鸭子类型(duck typing). 编程语言具有类型概念,例如Python中有数字类型.字符串类型.布尔类型,或者更加复杂的结构,例如元组tuple.列表list. ...
- Python执行机制
1.4 Python执行机制 Python中IDLE是其自带的集成开发工具(IDE:同时拥有编辑.编译.调试.运行等多种功能的集成工具),并且它也是Python自带的编译器和解释器. 1.4.1 Py ...
- C++ 文件加解密
通过文件二进制数据 与密钥进行异或处理,可加密文件 #ifndef __ENCRYPT__HEAD__ #define __ENCRYPT__HEAD__ #include <fstream&g ...
- Java Concurrency API 中的 Lock 接口(Lock interface) 是什么?对比同步它有什么优势?
Lock 接口比同步方法和同步块提供了更具扩展性的锁操作. 他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的 条件对象. 它的优势有: 可以使锁更公平 可以使线程在等待锁的时候响 ...
- 什么是可重入锁ReentrantLock?
举例来说明锁的可重入性 public class UnReentrant{ Lock lock = new Lock(); public void outer(){ lock.lock(); inne ...
- AQS 支持两种同步方式?
1.独占式 2.共享式 这样方便使用者实现不同类型的同步组件,独占式如 ReentrantLock,共享式如 Semaphore,CountDownLatch,组合式的如 ReentrantReadW ...
- 机器学习综合库gensim 简单搞定文本相似度
不废话直接代码吧 # 1.模块导入 import jieba import gensim from gensim import corpora from gensim import models fr ...
- servlet中的HttpServletRequest对象
HttpServletRequest对象表示客户端浏览器发起的请求,当客户端浏览器通过HTTP协议访问服务器时,Tomcat会将HTTP请求中的所有信息解析并封装在HttpServletRequest ...
- 外部晶振的使用原因与内部RC振荡器的使用方法 _
原因一 早些年,芯片的生产制作工艺也许还不能够将晶振做进芯片内部,但是现在可以了.这个问题主要还是实用性和成本决定的. 原因二 芯片和晶振的材料是不同的,芯片 (集成电路) 的材料是硅,而晶体则是 ...