Files
WCS/Cowain.TestProject/Services/ServiceCollectionExtensions.cs
2026-03-02 09:08:20 +08:00

425 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Cowain.Base.Abstractions.Navigation;
using Cowain.Base.Abstractions.Plugin;
using Cowain.Base.Attributes;
using Cowain.Base.DBContext;
using Cowain.Base.Helpers;
using Cowain.Base.IServices;
using Cowain.Base.Models;
using Cowain.Base.Models.Menu;
using Ke.Bee.Localization.Extensions;
using Ke.Bee.Localization.Options;
using Ke.Bee.Localization.Providers;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
namespace Cowain.TestProject.Services;
public static class ConfigureServices
{
private static List<Assembly>? _assemblies;
private static IOptions<AppSettings>? appSettings;
private static MenuConfigurationContext? menuConfigurationContext;
private static readonly List<IPlugin> _plugins = new();
private static ILogger<App>? _logger;
/// <summary>
/// 注册服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddServices(this IServiceCollection services, IConfiguration configuration, ILogger<App>? logger)
{
_logger = logger;
// 注册全局配置
services.AddSettings();
_assemblies = GetAssemblies();
GlobalData.Instance.AddOrUpdate("Assemblies", _assemblies);
services.AddMemoryCache();
//注入configuration
services.AddSingleton(configuration);
services.AddSingleton<IMessenger, WeakReferenceMessenger>();
// 注册数据库
services.AddDbContextService(configuration);
// 注册视图模型
services.AddViewModels();
// 注册应用菜单
services.AddMenus();
// 注册视图导航器
services.AddSingleton<IViewNavigator, DefaultViewNavigator>();
// 注册TransientAttribute
services.AddTransientAttributes();
// 注册SingletonAttribute
services.AddSingletonAttributes();
// 注册所有数据库服务
services.AddDbServices();
// 注册所有实现IHostedService的类
services.AddHostedServices();
//注册插件服务
services.AddPlugins();
// 注册本地化
services.AddLocalization();
return services;
}
private static List<Assembly> GetAssemblies()
{
string? pluginPath = appSettings?.Value.PluginPath;
// 获取当前已加载的程序集使用FullName作为唯一标识
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.ToDictionary(a => a.GetName().FullName);
// 创建结果列表,初始化为已加载的程序集
var assemblies = new List<Assembly>(loadedAssemblies.Values);
// 加载插件程序集
if (Directory.Exists(pluginPath))
{
var pluginFiles = Directory.GetFiles(pluginPath, "Plugin.*.dll", SearchOption.AllDirectories);
foreach (var file in pluginFiles)
{
try
{
// 获取程序集名称而不加载它
var assemblyName = AssemblyName.GetAssemblyName(file);
// 检查是否已加载同名程序集
if (!loadedAssemblies.ContainsKey(assemblyName.FullName))
{
// 加载程序集并添加到结果列表
var assembly = Assembly.LoadFrom(file);
assemblies.Add(assembly);
loadedAssemblies[assemblyName.FullName] = assembly;
}
}
catch (Exception ex)
{
// 处理加载错误(例如,非程序集文件、版本冲突等)
_logger?.LogError(ex, $"Error loading assembly from {file}: {ex.Message}");
}
}
}
return assemblies;
}
/// <summary>
/// 注册所有viewmodel
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddViewModels(this IServiceCollection services)
{
if (_assemblies == null)
{
return services;
}
var viewModelTypes = _assemblies.SelectMany(a => a.GetTypes())
.Where(t => t.Name.EndsWith("ViewModel") && t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(ObservableObject)));
foreach (var viewModelType in viewModelTypes)
{
try
{
services.AddTransient(viewModelType);
}
catch (Exception ex)
{
_logger?.LogError(ex, "ViewModel注入错误");
}
}
return services;
}
/// <summary>
/// 注册全局配置
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddSettings(this IServiceCollection services)
{
appSettings = Options.Create(new AppSettings
{
OutputPath = Path.Combine(AppContext.BaseDirectory, "Output"),
PluginPath = Path.Combine(AppContext.BaseDirectory, "Plugins")
});
services.AddSingleton(appSettings);
GlobalData.Instance.AddOrUpdate("AppSettings", appSettings.Value);
return services;
}
/// <summary>
/// 注册数据库
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddDbContextService(this IServiceCollection services, IConfiguration configuration)
{
var database_config = configuration.GetSection("Database")["db"];
if (string.IsNullOrEmpty(database_config))
{
_logger?.LogError("未配置数据库");
return services;
}
if (!Enum.TryParse<DataBaseType>(database_config.ToUpper(), out var cmdType))
{
return services;
}
// 配置EF Core日志过滤针对9.0.0版本)
services.AddLogging(loggingBuilder =>
{
// 过滤EF Core的Info级别日志只记录Warning及以上
loggingBuilder.AddFilter(
"Microsoft.EntityFrameworkCore",
LogLevel.Warning // 最低日志级别设为Warning
);
// 专门过滤数据库命令日志SQL执行语句等
loggingBuilder.AddFilter(
"Microsoft.EntityFrameworkCore.Database.Command",
LogLevel.Warning
);
});
services.AddPooledDbContextFactory<SqlDbContext>(options =>
{
switch (cmdType)
{
case DataBaseType.SQLITE:
var SqlLite_connection = configuration.GetConnectionString("SqlLiteConn");
options.UseSqlite(SqlLite_connection);
break;
case DataBaseType.SQLSERVER:
var SqlServer_connection = configuration.GetConnectionString("SqlServerConn");
options.UseSqlServer(SqlServer_connection);
break;
case DataBaseType.MYSQL:
var MySql_connection = configuration.GetConnectionString("MySqlConn");
options.UseMySql(MySql_connection, new MySqlServerVersion(new Version(8, 0, 26)));
break;
case DataBaseType.POSTGRES:
var Postgres_connection = configuration.GetConnectionString("PostGresConn");
options.UseNpgsql(Postgres_connection);
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
break;
}
#if DEBUG
options.EnableSensitiveDataLogging();
#endif
}, 100);
return services;
}
/// <summary>
/// 注册应用菜单
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddMenus(this IServiceCollection services)
{
// 从配置文件读取菜单注入到 DI 容器
var menuItems = JsonSerializer.Deserialize<List<MenuItem>>(
File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory, "Configs", "menus.json"))
);
menuConfigurationContext = new MenuConfigurationContext(menuItems);
services.AddSingleton(menuConfigurationContext);
return services;
}
/// <summary>
/// 注册插件服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddPlugins(this IServiceCollection services)
{
if (_assemblies == null)
{
return services;
}
var pluginTypes = _assemblies.SelectMany(a => a.GetTypes())
.Where(t => typeof(PluginBase).IsAssignableFrom(t) && !t.IsAbstract);
foreach (var type in pluginTypes)
{
if (Activator.CreateInstance(type) is IPlugin plugin)
{
plugin.RegisterServices(services, _assemblies);
//只是注入插件,不需要配置菜单
//plugin.ConfigureMenu(menuConfigurationContext);
_plugins.Add(plugin);
}
}
return services;
}
public static void InitializePlugins(this IServiceProvider serviceProvider)
{
foreach (var plugin in _plugins)
{
plugin.Initialize(serviceProvider);
}
}
public static void ShutDownPlugins(this IServiceProvider serviceProvider)
{
foreach (var plugin in _plugins)
{
plugin.Shutdown();
}
}
/// <summary>
/// 注册所有数据库服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddDbServices(this IServiceCollection services)
{
if (_assemblies == null)
{
return services;
}
var types = _assemblies.SelectMany(a => a.GetTypes())
.Where(t => typeof(IBaseService).IsAssignableFrom(t) && t != typeof(IBaseService));
var implementTypes = types.Where(x => x.IsClass).ToArray();
var interfaceTypes = types.Where(x => x.IsInterface).ToArray();
foreach (var implementType in implementTypes)
{
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
if (interfaceType != null)
services.AddScoped(interfaceType, implementType);
}
return services;
}
/// <summary>
/// 注册所有TransientAttribute
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddTransientAttributes(this IServiceCollection services)
{
if (_assemblies == null)
{
return services;
}
var transientTypes = _assemblies.SelectMany(a => a.GetTypes())
.Where(t => t.GetCustomAttributes(typeof(TransientAttribute), true).Any() && t.IsClass && !t.IsAbstract);
foreach (var transientType in transientTypes)
{
services.AddTransient(transientType);
}
return services;
}
/// <summary>
/// 注册所有SingletonAttribute
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddSingletonAttributes(this IServiceCollection services)
{
if (_assemblies == null)
{
return services;
}
var transientTypes = _assemblies.SelectMany(a => a.GetTypes())
.Where(t => t.GetCustomAttributes(typeof(SingletonAttribute), true).Any() && t.IsClass && !t.IsAbstract);
foreach (var transientType in transientTypes)
{
services.AddSingleton(transientType);
}
return services;
}
/// <summary>
/// 注册所有实现IHostedService的类
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddHostedServices(this IServiceCollection services)
{
if (_assemblies == null)
{
return services;
}
var hostedServiceTypes = _assemblies.SelectMany(a => a.GetTypes())
.Where(t => typeof(IHostedService).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract);
// 获取 AddHostedService 方法的泛型定义
var addHostedServiceMethod = typeof(ServiceCollectionHostedServiceExtensions)
.GetMethods()
.First(m => m.Name == "AddHostedService" && m.IsGenericMethod && m.GetParameters().Length == 1);
foreach (var hostedServiceType in hostedServiceTypes)
{
try
{
// 构造泛型方法并调用
var genericMethod = addHostedServiceMethod.MakeGenericMethod(hostedServiceType);
genericMethod.Invoke(null, new object[] { services });
}
catch (Exception ex)
{
_logger?.LogError(ex, $"注册后台服务 {hostedServiceType.FullName} 失败");
}
}
return services;
}
/// <summary>
/// 注册本地化
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
private static IServiceCollection AddLocalization(this IServiceCollection services)
{
services.AddLocalization<AvaloniaJsonLocalizationProvider>(() =>
{
var options = new AvaloniaLocalizationOptions(
// 支持的本地化语言文化
[
new("en-US"),
new("zh-CN")
],
// defaultCulture, 用于设置当前文化currentCulture不在 cultures 列表中时的情况以及作为缺失的本地化条目的备用文化fallback culture
new CultureInfo("en-US"),
// currentCulture 在基础设施加载时设置,可以从应用程序设置或其他地方获取
Thread.CurrentThread.CurrentCulture,
// 包含本地化 JSON 文件的资源路径
$"{typeof(App).Namespace}/Assets/i18n");
return options;
});
return services;
}
}