我们在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的,所以我们只能把这部分内容全部重写。

我们创建自己的MyViewCompilerProviderMyViewCompiler。由于.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源码里的构造函数拆分了,拆出了一个PublicModify方法。

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

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

最新文章

  1. sudo 出现unable to resolve host 解决方法
  2. 解决mysql登陆时出现“ERROR 2002 (HY000): Can&#39;t connect to local MySQL server through socket &#39;/var/run/mysql/mysql.sock&#39; (2)”
  3. Android高手进阶:Adapter深入理解与优化
  4. uva11029 - Leading and Trailing
  5. js正则验证&quot;汉字&quot;
  6. php制作数据字典
  7. ubuntu下包管理器apt-get常用命令
  8. STM32GPIO口8种模式细致分析(类比51单片机)
  9. Go 数据结构--二分查找树
  10. asp.net MVC NPOI导出excel通用
  11. Docker构建一个node镜像
  12. list映射
  13. HTC Vive 基础入门 基于Unreal Engine 4引擎
  14. python高并发的解决方案
  15. cstdlib和stdlib.h区别
  16. C中const 定义常量的使用
  17. Servlet基本_セッション属性
  18. 第一天:javascript实现界面运算及循环语句跳转语句
  19. java正则表达式:验证字符串数字
  20. Swing 是一个为Java设计的GUI工具包

热门文章

  1. Python 的排序方法 sort 和 sorted 的区别
  2. 如何理解Node.js和JavaScript的关系
  3. Java基础(中)
  4. Java如何使用实时流式计算处理?
  5. vivo 商品中台的可视化微前端实践
  6. Python集成开发工具(IDE)推荐
  7. synchronized 关键字的用法?
  8. JS字符串格式化
  9. 使用conda管理python环境和包
  10. 学习Apache(三)