修改 CloudProduct ImageCache 项目改为 插件

This commit is contained in:
chfei committed 2026-10-22 15:56:55 +08:00
1 parent 5ff52dea92
commit 292cb8638c
26 files changed
+417 -282

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace MAX.Plugins;
/// <summary>独立插件入口。实现类必须具有无参构造函数。</summary>
public interface IPlugin
{
string Id { get; }
int Priority { get; }
void ConfigureServices(IServiceCollection services, PluginContext context);
void Configure(IApplicationBuilder app, PluginContext context);
}
+3
View File
@@ -0,0 +1,3 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup><FrameworkReference Include="Microsoft.AspNetCore.App" /></ItemGroup>
</Project>
+32
View File
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace MAX.Plugins;
public abstract class PluginBase : IPlugin
{
public abstract string Id { get; }
public virtual int Priority => 0;
protected PluginContext Context { get; private set; } = null!;
public void ConfigureServices(IServiceCollection services, PluginContext context)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(context);
if (!string.Equals(Id, context.Id, StringComparison.Ordinal))
throw new ArgumentException("插件上下文标识不匹配。", nameof(context));
if (Context is not null) throw new InvalidOperationException($"插件 {Id} 已注册。");
Context = context;
RegisterServices(services);
}
public void Configure(IApplicationBuilder app, PluginContext context)
{
if (!ReferenceEquals(Context, context))
throw new InvalidOperationException($"插件 {Id} 尚未注册或上下文不匹配。");
ConfigureApplication(app);
}
protected abstract void RegisterServices(IServiceCollection services);
protected virtual void ConfigureApplication(IApplicationBuilder app) { }
protected string RequireConfiguration(string key) => Context.RequireConfiguration(key);
}
+39
View File
@@ -0,0 +1,39 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace MAX.Plugins;
/// <summary>插件独立配置,不合并宿主或其他插件配置。</summary>
public sealed class PluginContext : IDisposable
{
private int disposed;
public string Id { get; }
public string DirectoryPath { get; }
public IHostEnvironment Environment { get; }
public IConfigurationRoot Configuration { get; }
public PluginContext(string id, string directoryPath, IHostEnvironment environment)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
ArgumentNullException.ThrowIfNull(environment);
Id = id;
DirectoryPath = Path.GetFullPath(directoryPath);
Environment = environment;
Configuration = new ConfigurationBuilder()
.SetBasePath(DirectoryPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true, reloadOnChange: false)
.AddEnvironmentVariables($"Plugins__{id}__")
.Build();
}
public string RequireConfiguration(string key) =>
!string.IsNullOrWhiteSpace(Configuration[key]) ? Configuration[key]!
: throw new InvalidOperationException($"插件 {Id} 缺少配置 {key},请检查 {Path.Combine(DirectoryPath, "appsettings.json")}。");
public void Dispose()
{
if (Interlocked.Exchange(ref disposed, 1) == 0)
(Configuration as IDisposable)?.Dispose();
}
}