osharp多租户方案

osharp多租户方案

租户信息

C# 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OSharp.MultiTenancy
{
    /// <summary>
    /// 租户信息
    /// </summary>
    public class TenantInfo
    {
        /// <summary>
        /// 获取或设置 租户ID
        /// </summary>
        public string TenantId { get; set; }

        /// <summary>
        /// 获取或设置 租户名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 获取或设置 租户主机
        /// </summary>
        public string Host { get; set; }

        /// <summary>
        /// 获取或设置 连接字符串
        /// </summary>
        public string ConnectionString { get; set; }

        /// <summary>
        /// 获取或设置 是否启用
        /// </summary>
        public bool IsEnabled { get; set; } = true;
    }
}

定义租户访问器实现

C# 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OSharp.MultiTenancy
{
    // 租户访问器实现
    public class TenantAccessor : ITenantAccessor
    {
        public TenantInfo CurrentTenant { get; set; }
    }

    /// <summary>
    /// 使用 AsyncLocal<T> 实现的租户信息访问器
    /// </summary>
    public class AsyncLocalTenantAccessor : ITenantAccessor
    {
        // 使用 AsyncLocal<T> 存储租户信息
        private static readonly AsyncLocal<TenantInfo> _currentTenant = new AsyncLocal<TenantInfo>();

        /// <summary>
        /// 获取或设置当前租户
        /// </summary>
        public TenantInfo CurrentTenant
        {
            get => _currentTenant.Value;
            set => _currentTenant.Value = value;
        }
    }
}

修改MultiTenantConnectionStringProvider使用选定租户的数据库连接

C# 复制代码
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using OSharp.Entity;
using OSharp.MultiTenancy;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OSharp.Entity
{
    /// <summary>
    /// 租户数据库选择器,实现IConnectionStringProvider接口
    /// </summary>
    public class MultiTenantConnectionStringProvider : IConnectionStringProvider
    {
        private readonly IConfiguration _configuration;
        private readonly ITenantAccessor _tenantAccessor;
        private readonly ILogger<MultiTenantConnectionStringProvider> _logger;
        private readonly ConcurrentDictionary<string, string> _connectionStringCache = new ConcurrentDictionary<string, string>();

        public MultiTenantConnectionStringProvider(
            IConfiguration configuration,
            ITenantAccessor tenantAccessor,
            ILogger<MultiTenantConnectionStringProvider> logger)
        {
            _configuration = configuration;
            _tenantAccessor = tenantAccessor;
            _logger = logger;
        }

        /// <summary>
        /// 获取数据库连接字符串
        /// </summary>
        public string GetConnectionString(Type dbContextType)
        {
            if(dbContextType.Name == "TenantDbContext")
            {
                return _configuration.GetConnectionString("Tenant");
            }
            // 获取当前租户
            TenantInfo tenant = _tenantAccessor.CurrentTenant;
            
            // 获取DbContext的连接字符串名称
            string connectionStringName = dbContextType.Name.Replace("DbContext", "");
            
            // 构建缓存键
            string cacheKey = tenant?.TenantId + "_" + connectionStringName;
            
            // 尝试从缓存获取连接字符串
            if (!string.IsNullOrEmpty(cacheKey) && _connectionStringCache.TryGetValue(cacheKey, out string cachedConnectionString))
            {
                return cachedConnectionString;
            }

            string connectionString = null;

            // 如果有租户信息
            if (tenant != null)
            {
                // 1. 首先尝试使用租户自己的连接字符串
                if (!string.IsNullOrEmpty(tenant.ConnectionString))
                {
                    connectionString = tenant.ConnectionString;
                    _logger.LogDebug("使用租户 {TenantId} 的连接字符串", tenant.TenantId);
                }
                else
                {
                    // 2. 尝试从配置中获取特定租户的特定DbContext连接字符串
                    string tenantConnectionStringKey = $"ConnectionStrings:{tenant.TenantId}:{connectionStringName}";
                    connectionString = _configuration[tenantConnectionStringKey];

                    // 3. 尝试从配置中获取特定租户的默认连接字符串
                    if (string.IsNullOrEmpty(connectionString))
                    {
                        string tenantDefaultConnectionStringKey = $"ConnectionStrings:{tenant.TenantId}:Default";
                        connectionString = _configuration[tenantDefaultConnectionStringKey];
                        
                        if (!string.IsNullOrEmpty(connectionString))
                        {
                            _logger.LogDebug("使用租户 {TenantId} 的默认连接字符串", tenant.TenantId);
                        }
                    }
                    else
                    {
                        _logger.LogDebug("使用租户 {TenantId} 的 {DbContext} 连接字符串", tenant.TenantId, connectionStringName);
                    }
                }
            }

            // 4. 如果仍未找到连接字符串,则使用应用程序的默认连接字符串
            if (string.IsNullOrEmpty(connectionString))
            {
                // 尝试获取特定DbContext的连接字符串
                connectionString = _configuration.GetConnectionString(connectionStringName);
                
                // 如果没有特定DbContext的连接字符串,则使用默认连接字符串
                if (string.IsNullOrEmpty(connectionString))
                {
                    connectionString = _configuration.GetConnectionString("Default");
                    _logger.LogDebug("使用应用程序默认连接字符串");
                }
                else
                {
                    _logger.LogDebug("使用应用程序 {DbContext} 连接字符串", connectionStringName);
                }
            }

            // 缓存连接字符串
            if (!string.IsNullOrEmpty(cacheKey) && !string.IsNullOrEmpty(connectionString))
            {
                _connectionStringCache[cacheKey] = connectionString;
            }

            return connectionString;
        }
    }
} 

修改Program.cs

C# 复制代码
// -----------------------------------------------------------------------
//  <copyright file="Program.cs" company="OSharp开源团队">
//      Copyright (c) 2014-2018 OSharp. All rights reserved.
//  </copyright>
//  <site>http://www.osharp.org</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2018-06-27 4:50</last-date>
// -----------------------------------------------------------------------

using AspectCore.Extensions.Hosting;

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using OSharp.Core;
using Liuliu.Demo.Authorization;
using Liuliu.Demo.Identity;
using Liuliu.Demo.Infos;
using Liuliu.Demo.Systems;
using Liuliu.Demo.Web.Startups;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using OSharp.AspNetCore;
using OSharp.AspNetCore.Routing;
using OSharp.AutoMapper;
using OSharp.Log4Net;
using OSharp.MiniProfiler;
using OSharp.Swagger;
using Microsoft.AspNetCore.Http;
using OSharp.Entity;
using OSharp.MultiTenancy;
using OSharp.Entity.SqlServer;
using OSharp.Caching;

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<FileTenantStoreOptions>(options =>
{
    options.FilePath = "App_Data/tenants.json";
});

// 添加服务到容器
// 注册租户访问器
builder.Services.AddScoped<ITenantAccessor, AsyncLocalTenantAccessor>();
builder.Services.AddScoped<ITenantProvider, HttpTenantProvider>();

// 注册租户存储
//builder.Services.AddSingleton<ITenantStore, ConfigurationTenantStore>();
//builder.Services.AddSingleton<ITenantStore, FileTenantStore>();
builder.Services.AddSingleton<ITenantStore, DatabaseTenantStore>();

builder.Services.AddHttpContextAccessor(); // 注册 IHttpContextAccessor

// 注册租户数据库迁移器
builder.Services.AddSingleton<TenantDatabaseMigrator>();

// 替换默认的连接字符串提供者
//builder.Services.AddScoped<IConnectionStringProvider, MultiTenantConnectionStringProvider>();
builder.Services.Replace<IConnectionStringProvider, MultiTenantConnectionStringProvider>(ServiceLifetime.Scoped);

builder.Services.AddSingleton<ICacheKeyGenerator, StringCacheKeyGenerator>();
builder.Services.AddSingleton<IGlobalCacheKeyGenerator>(provider =>
    provider.GetRequiredService<ICacheKeyGenerator>() as IGlobalCacheKeyGenerator);

//builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddOSharp()
    .AddPack<Log4NetPack>()
    .AddPack<AutoMapperPack>()
    .AddPack<EndpointsPack>()
    .AddPack<MiniProfilerPack>()
    .AddPack<SwaggerPack>()
    //.AddPack<RedisPack>()
    .AddPack<AuthenticationPack>()
    .AddPack<FunctionAuthorizationPack>()
    .AddPack<DataAuthorizationPack>()
    .AddPack<SqlServerDefaultDbContextMigrationPack>()
    .AddPack<TenantDbContextMigrationPack>()
    .AddPack<AuditPack>()
    .AddPack<InfosPack>();

var app = builder.Build();

// 在请求管道中注册 TenantMiddleware
// 注意:应该在 UseRouting 之后,但在 UseAuthentication 和 UseAuthorization 之前注册
app.UseRouting();

// 添加多租户中间件
app.UseMiddleware<TenantMiddleware>();

// 使用 OSharp
app.UseOSharp();

using (var scope = app.Services.CreateScope())
{
    var migrator = scope.ServiceProvider.GetRequiredService<TenantDatabaseMigrator>();
    await migrator.MigrateAllTenantsAsync();
}

app.Run();

将所有的数据库配置文件中的HasName改成HasDatabaseName

将项目设置为.net8.0

租户存储管理

C# 复制代码
//ITenantStore
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OSharp.MultiTenancy
{
    /// <summary>
    /// 定义租户存储
    /// </summary>
    public interface ITenantStore
    {
        /// <summary>
        /// 获取所有租户
        /// </summary>
        Task<IEnumerable<TenantInfo>> GetAllTenantsAsync();
        
        /// <summary>
        /// 根据租户ID获取租户
        /// </summary>
        Task<TenantInfo> GetTenantAsync(string tenantId);
        
        /// <summary>
        /// 根据主机名获取租户
        /// </summary>
        Task<TenantInfo> GetTenantByHostAsync(string host);
        
        /// <summary>
        /// 保存租户信息
        /// </summary>
        Task<bool> SaveTenantAsync(TenantInfo tenant);
        
        /// <summary>
        /// 删除租户
        /// </summary>
        Task<bool> DeleteTenantAsync(string tenantId);
    }
}
C# 复制代码
//FileTenantStore
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace OSharp.MultiTenancy
{
    /// <summary>
    /// 基于文件的租户存储实现,将租户信息保存在JSON文件中
    /// </summary>
    public class FileTenantStore : ITenantStore
    {
        private readonly FileTenantStoreOptions _options;
        private readonly ILogger<FileTenantStore> _logger;
        private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
        private Dictionary<string, TenantInfo> _tenants = new Dictionary<string, TenantInfo>();
        private bool _isInitialized = false;

        public FileTenantStore(
            IOptions<FileTenantStoreOptions> options,
            ILogger<FileTenantStore> logger)
        {
            _options = options.Value;
            _logger = logger;
        }

        /// <summary>
        /// 获取所有启用的租户
        /// </summary>
        public async Task<IEnumerable<TenantInfo>> GetAllTenantsAsync()
        {
            await EnsureInitializedAsync();
            return _tenants.Values.Where(t => t.IsEnabled).ToList();
        }

        /// <summary>
        /// 根据租户ID获取租户信息
        /// </summary>
        public async Task<TenantInfo> GetTenantAsync(string tenantId)
        {
            if (string.IsNullOrEmpty(tenantId))
            {
                return null;
            }

            await EnsureInitializedAsync();

            if (_tenants.TryGetValue(tenantId, out var tenant) && tenant.IsEnabled)
            {
                return tenant;
            }

            return null;
        }

        /// <summary>
        /// 根据主机名获取租户信息
        /// </summary>
        public async Task<TenantInfo> GetTenantByHostAsync(string host)
        {
            if (string.IsNullOrEmpty(host))
            {
                return null;
            }

            await EnsureInitializedAsync();

            return _tenants.Values
                .FirstOrDefault(t => t.IsEnabled &&
                    (t.Host.Equals(host, StringComparison.OrdinalIgnoreCase) ||
                     host.EndsWith("." + t.Host, StringComparison.OrdinalIgnoreCase)));
        }

        /// <summary>
        /// 保存租户信息
        /// </summary>
        public async Task<bool> SaveTenantAsync(TenantInfo tenant)
        {
            if (tenant == null || string.IsNullOrEmpty(tenant.TenantId))
            {
                return false;
            }

            await EnsureInitializedAsync();

            await _semaphore.WaitAsync();
            try
            {
                // 更新内存中的租户信息
                _tenants[tenant.TenantId] = tenant;

                // 保存到文件
                await SaveToFileAsync();
                
                _logger.LogInformation("已保存租户信息: {TenantId}, {Name}, {Host}", 
                    tenant.TenantId, tenant.Name, tenant.Host);
                
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "保存租户信息失败: {TenantId}", tenant.TenantId);
                return false;
            }
            finally
            {
                _semaphore.Release();
            }
        }

        /// <summary>
        /// 删除租户信息
        /// </summary>
        public async Task<bool> DeleteTenantAsync(string tenantId)
        {
            if (string.IsNullOrEmpty(tenantId))
            {
                return false;
            }

            await EnsureInitializedAsync();

            await _semaphore.WaitAsync();
            try
            {
                if (!_tenants.ContainsKey(tenantId))
                {
                    return false;
                }

                // 从内存中移除租户
                _tenants.Remove(tenantId);

                // 保存到文件
                await SaveToFileAsync();
                
                _logger.LogInformation("已删除租户: {TenantId}", tenantId);
                
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除租户失败: {TenantId}", tenantId);
                return false;
            }
            finally
            {
                _semaphore.Release();
            }
        }

        /// <summary>
        /// 确保已初始化
        /// </summary>
        private async Task EnsureInitializedAsync()
        {
            if (_isInitialized)
            {
                return;
            }

            await _semaphore.WaitAsync();
            try
            {
                if (_isInitialized)
                {
                    return;
                }

                await LoadFromFileAsync();
                _isInitialized = true;
            }
            finally
            {
                _semaphore.Release();
            }
        }

        /// <summary>
        /// 从文件加载租户信息
        /// </summary>
        private async Task LoadFromFileAsync()
        {
            string filePath = GetFilePath();
            
            if (!File.Exists(filePath))
            {
                _logger.LogInformation("租户配置文件不存在,将创建新文件: {FilePath}", filePath);
                _tenants = new Dictionary<string, TenantInfo>();
                await SaveToFileAsync(); // 创建空文件
                return;
            }

            try
            {
                string json = await File.ReadAllTextAsync(filePath);
                var tenants = JsonSerializer.Deserialize<List<TenantInfo>>(json, new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                });

                _tenants = tenants.ToDictionary(t => t.TenantId);
                _logger.LogInformation("已从文件加载 {Count} 个租户", _tenants.Count);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "从文件加载租户信息失败: {FilePath}", filePath);
                _tenants = new Dictionary<string, TenantInfo>();
            }
        }

        /// <summary>
        /// 保存租户信息到文件
        /// </summary>
        private async Task SaveToFileAsync()
        {
            string filePath = GetFilePath();
            string directoryPath = Path.GetDirectoryName(filePath);
            
            if (!Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }

            try
            {
                var tenants = _tenants.Values.ToList();
                string json = JsonSerializer.Serialize(tenants, new JsonSerializerOptions
                {
                    WriteIndented = true
                });

                await File.WriteAllTextAsync(filePath, json);
                _logger.LogDebug("已将 {Count} 个租户信息保存到文件: {FilePath}", tenants.Count, filePath);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "保存租户信息到文件失败: {FilePath}", filePath);
                throw;
            }
        }

        /// <summary>
        /// 获取文件路径
        /// </summary>
        private string GetFilePath()
        {
            return Path.GetFullPath(_options.FilePath);
        }
    }

    /// <summary>
    /// 文件租户存储选项
    /// </summary>
    public class FileTenantStoreOptions
    {
        /// <summary>
        /// 租户配置文件路径,默认为 "App_Data/tenants.json"
        /// </summary>
        public string FilePath { get; set; } = "App_Data/tenants.json";
    }
} 
C# 复制代码
//ConfigurationTenantStore
using Microsoft.Extensions.Configuration;
using OSharp.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OSharp.MultiTenancy
{
    /// <summary>
    /// 基于配置文件的租户存储实现
    /// </summary>
    public class ConfigurationTenantStore : ITenantStore
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<ConfigurationTenantStore> _logger;
        private readonly Dictionary<string, TenantInfo> _tenants = new Dictionary<string, TenantInfo>();

        public ConfigurationTenantStore(
            IConfiguration configuration,
            ILogger<ConfigurationTenantStore> logger)
        {
            _configuration = configuration;
            _logger = logger;

            // 从配置中加载租户信息
            LoadTenantsFromConfiguration();
        }

        public Task<bool> DeleteTenantAsync(string tenantId)
        {
            throw new NotImplementedException();
        }

        public Task<IEnumerable<TenantInfo>> GetAllTenantsAsync()
        {
            return Task.FromResult(_tenants.Values.Where(t => t.IsEnabled).AsEnumerable());
        }

        public Task<TenantInfo> GetTenantAsync(string tenantId)
        {
            if (string.IsNullOrEmpty(tenantId) || !_tenants.TryGetValue(tenantId, out var tenant) || !tenant.IsEnabled)
            {
                return Task.FromResult<TenantInfo>(null);
            }

            return Task.FromResult(tenant);
        }

        public Task<TenantInfo> GetTenantByHostAsync(string host)
        {
            if (string.IsNullOrEmpty(host))
            {
                return Task.FromResult<TenantInfo>(null);
            }

            var tenant = _tenants.Values
                .FirstOrDefault(t => t.IsEnabled &&
                    (t.Host.Equals(host, StringComparison.OrdinalIgnoreCase) ||
                     host.EndsWith("." + t.Host, StringComparison.OrdinalIgnoreCase)));

            return Task.FromResult(tenant);
        }

        public Task<bool> SaveTenantAsync(TenantInfo tenant)
        {
            // 配置文件实现通常是只读的,不支持保存
            _logger.LogWarning("ConfigurationTenantStore 不支持保存租户信息");
            return Task.FromResult(false);
        }

        private void LoadTenantsFromConfiguration()
        {
            var tenantsSection = _configuration.GetSection("Tenants");
            if (!tenantsSection.Exists())
            {
                _logger.LogWarning("在配置中未找到 'Tenants' 节点");
                return;
            }

            foreach (var tenantSection in tenantsSection.GetChildren())
            {
                var tenant = new TenantInfo
                {
                    TenantId = tenantSection.Key,
                    Name = tenantSection["Name"],
                    Host = tenantSection["Host"],
                    ConnectionString = tenantSection["ConnectionString"],
                    IsEnabled = tenantSection.GetValue<bool>("IsEnabled", true)
                };

                if (!string.IsNullOrEmpty(tenant.TenantId) && !string.IsNullOrEmpty(tenant.Host))
                {
                    _tenants[tenant.TenantId] = tenant;
                    _logger.LogInformation("已加载租户: {TenantId}, {Name}, {Host}", tenant.TenantId, tenant.Name, tenant.Host);
                }
                else
                {
                    _logger.LogWarning("租户配置不完整,已跳过: {TenantId}", tenant.TenantId);
                }
            }
        }
    }
}

基于数据库的租户存储实现

C# 复制代码
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OSharp.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading.Tasks;

namespace OSharp.Entity
{
    /// <summary>
    /// 基于数据库的租户存储实现
    /// </summary>
    public class DatabaseTenantStore : ITenantStore
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IMemoryCache _cache;
        private readonly ILogger<DatabaseTenantStore> _logger;
        private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(10);
        private const string CacheKeyPrefix = "Tenant_";
        private const string AllTenantsCacheKey = "AllTenants";
        private readonly IConfiguration _configuration;

        public DatabaseTenantStore(
            IServiceProvider serviceProvider,
            IMemoryCache cache,
            IConfiguration configuration,
            ILogger<DatabaseTenantStore> logger)
        {
            _serviceProvider = serviceProvider;
            _cache = cache;
            _logger = logger;
            _configuration = configuration;
        }

        /// <summary>
        /// 获取所有启用的租户
        /// </summary>
        public async Task<IEnumerable<TenantInfo>> GetAllTenantsAsync()
        {
            // 尝试从缓存获取
            if (_cache.TryGetValue(AllTenantsCacheKey, out IEnumerable<TenantInfo> cachedTenants))
            {
                return cachedTenants;
            }

            // 从数据库获取
            using var scope = _serviceProvider.CreateScope();
            var TenantRepository = scope.ServiceProvider.GetRequiredService<IRepository<TenantEntity,Guid>>();
            var tenantEntities = await TenantRepository.QueryAsNoTracking().Where(t => t.IsEnabled).ToListAsync();

            if(tenantEntities.Count == 0)
            {
                await ImportFromConfigurationAsync(_configuration);
            }
            tenantEntities = await TenantRepository.QueryAsNoTracking().Where(t => t.IsEnabled).ToListAsync();

            var tenants = tenantEntities.Select(MapToTenantInfo).ToList();

            // 缓存结果
            _cache.Set(AllTenantsCacheKey, tenants, _cacheExpiration);

            return tenants;
        }

        /// <summary>
        /// 根据租户ID获取租户
        /// </summary>
        public async Task<TenantInfo> GetTenantAsync(string tenantId)
        {
            if (string.IsNullOrEmpty(tenantId))
            {
                return null;
            }

            // 尝试从缓存获取
            string cacheKey = $"{CacheKeyPrefix}{tenantId}";
            if (_cache.TryGetValue(cacheKey, out TenantInfo cachedTenant))
            {
                return cachedTenant;
            }

            // 从数据库获取
            using var scope = _serviceProvider.CreateScope();
            var TenantRepository = scope.ServiceProvider.GetRequiredService<IRepository<TenantEntity, Guid>>();
            var tenantEntity = await TenantRepository.QueryAsNoTracking().Where(t => t.TenantId == tenantId).FirstOrDefaultAsync();

            if (tenantEntity == null)
            {
                return null;
            }

            var tenant = MapToTenantInfo(tenantEntity);

            // 缓存结果
            _cache.Set(cacheKey, tenant, _cacheExpiration);

            return tenant;
        }

        /// <summary>
        /// 根据主机名获取租户
        /// </summary>
        public async Task<TenantInfo> GetTenantByHostAsync(string host)
        {
            if (string.IsNullOrEmpty(host))
            {
                return null;
            }

            // 获取所有租户
            var allTenants = await GetAllTenantsAsync();

            // 查找匹配的租户
            return allTenants.FirstOrDefault(t =>
                t.Host.Equals(host, StringComparison.OrdinalIgnoreCase) ||
                host.EndsWith("." + t.Host, StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 保存租户信息
        /// </summary>
        public async Task<bool> SaveTenantAsync(TenantInfo tenant)
        {
            if (tenant == null || string.IsNullOrEmpty(tenant.TenantId))
            {
                return false;
            }

            using var scope = _serviceProvider.CreateScope();
            var TenantRepository = scope.ServiceProvider.GetRequiredService<IRepository<TenantEntity, Guid>>();
            var existingEntity = await TenantRepository.Query().Where(t => t.TenantId == tenant.TenantId).FirstOrDefaultAsync();
            try
            {
                if (existingEntity == null)
                {
                    // 创建新租户
                    var newEntity = new TenantEntity
                    {
                        TenantId = tenant.TenantId,
                        Name = tenant.Name,
                        Host = tenant.Host,
                        ConnectionString = tenant.ConnectionString,
                        IsEnabled = tenant.IsEnabled,
                        CreatedTime = DateTime.Now
                    };

                    await TenantRepository.InsertAsync(newEntity);
                    _logger.LogInformation("创建新租户: {TenantId}, {Name}", tenant.TenantId, tenant.Name);
                }
                else
                {
                    // 更新现有租户
                    existingEntity.Name = tenant.Name;
                    existingEntity.Host = tenant.Host;
                    existingEntity.ConnectionString = tenant.ConnectionString;
                    existingEntity.IsEnabled = tenant.IsEnabled;
                    existingEntity.UpdatedTime = DateTime.Now;

                    await TenantRepository.UpdateAsync(existingEntity);
                    _logger.LogInformation("更新租户: {TenantId}, {Name}", tenant.TenantId, tenant.Name);
                }

                // 清除缓存
                InvalidateCache(tenant.TenantId);

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "保存租户信息失败: {TenantId}", tenant.TenantId);
                return false;
            }
        }

        /// <summary>
        /// 删除租户
        /// </summary>
        public async Task<bool> DeleteTenantAsync(string tenantId)
        {
            if (string.IsNullOrEmpty(tenantId))
            {
                return false;
            }

            using var scope = _serviceProvider.CreateScope();
            var TenantRepository = scope.ServiceProvider.GetRequiredService<IRepository<TenantEntity, Guid>>();
            var tenantEntity = await TenantRepository.Query().Where(t => t.TenantId == tenantId).FirstOrDefaultAsync();

            if (tenantEntity == null)
            {
                return false;
            }

            try
            {
                await TenantRepository.DeleteAsync(tenantEntity);

                // 清除缓存
                InvalidateCache(tenantId);

                _logger.LogInformation("删除租户: {TenantId}", tenantId);
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除租户失败: {TenantId}", tenantId);
                return false;
            }
        }

        /// <summary>
        /// 从配置导入租户信息到数据库
        /// </summary>
        public async Task<int> ImportFromConfigurationAsync(IConfiguration configuration)
        {
            var tenantsSection = configuration.GetSection("Tenants");
            if (!tenantsSection.Exists())
            {
                _logger.LogWarning("在配置中未找到 'Tenants' 节点");
                return 0;
            }

            int importCount = 0;
            foreach (var tenantSection in tenantsSection.GetChildren())
            {
                var tenant = new TenantInfo
                {
                    TenantId = tenantSection.Key,
                    Name = tenantSection["Name"],
                    Host = tenantSection["Host"],
                    ConnectionString = tenantSection["ConnectionString"],
                    IsEnabled = tenantSection.GetValue<bool>("IsEnabled", true)
                };

                // 如果ConnectionString为空,尝试从ConnectionStrings节点获取
                if (string.IsNullOrEmpty(tenant.ConnectionString))
                {
                    string connectionStringKey = $"ConnectionStrings:{tenant.TenantId}:Default";
                    tenant.ConnectionString = configuration[connectionStringKey];
                    
                    // 如果仍然为空,尝试直接获取租户ID对应的连接字符串
                    if (string.IsNullOrEmpty(tenant.ConnectionString))
                    {
                        tenant.ConnectionString = configuration.GetConnectionString(tenant.TenantId);
                    }
                }

                if (!string.IsNullOrEmpty(tenant.TenantId) && !string.IsNullOrEmpty(tenant.Host))
                {
                    bool success = await SaveTenantAsync(tenant);
                    if (success)
                    {
                        importCount++;
                        _logger.LogInformation("已导入租户: {TenantId}, {Name}, {Host}", 
                            tenant.TenantId, tenant.Name, tenant.Host);
                    }
                }
                else
                {
                    _logger.LogWarning("租户配置不完整,已跳过: {TenantId}", tenant.TenantId);
                }
            }

            // 清除所有缓存
            _cache.Remove(AllTenantsCacheKey);

            return importCount;
        }

        /// <summary>
        /// 将实体映射为租户信息
        /// </summary>
        private TenantInfo MapToTenantInfo(TenantEntity entity)
        {
            return new TenantInfo
            {
                TenantId = entity.TenantId,
                Name = entity.Name,
                Host = entity.Host,
                ConnectionString = entity.ConnectionString,
                IsEnabled = entity.IsEnabled
            };
        }

        /// <summary>
        /// 使缓存失效
        /// </summary>
        private void InvalidateCache(string tenantId)
        {
            _cache.Remove($"{CacheKeyPrefix}{tenantId}");
            _cache.Remove(AllTenantsCacheKey);
        }
    }
} 

TenantEntity

c# 复制代码
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using OSharp.Entity;

namespace OSharp.MultiTenancy
{
    /// <summary>
    /// 租户数据库实体
    /// </summary>
    [Description("租户信息")]
    public class TenantEntity : EntityBase<Guid>
    {
        /// <summary>
        /// 获取或设置 租户ID
        /// </summary>
        [Required, StringLength(50)]
        [Description("租户ID")]
        public string TenantId { get; set; }

        /// <summary>
        /// 获取或设置 租户名称
        /// </summary>
        [Required, StringLength(100)]
        [Description("租户名称")]
        public string Name { get; set; }

        /// <summary>
        /// 获取或设置 租户主机
        /// </summary>
        [Required, StringLength(100)]
        [Description("租户主机")]
        public string Host { get; set; }

        /// <summary>
        /// 获取或设置 连接字符串
        /// </summary>
        [StringLength(1000)]
        [Description("连接字符串")]
        public string ConnectionString { get; set; }

        /// <summary>
        /// 获取或设置 是否启用
        /// </summary>
        [Description("是否启用")]
        public bool IsEnabled { get; set; } = true;

        /// <summary>
        /// 获取或设置 创建时间
        /// </summary>
        [Description("创建时间")]
        public DateTime CreatedTime { get; set; } = DateTime.Now;

        /// <summary>
        /// 获取或设置 更新时间
        /// </summary>
        [Description("更新时间")]
        public DateTime? UpdatedTime { get; set; }
    }
} 

TenantDbContext

C# 复制代码
using Microsoft.EntityFrameworkCore;
using OSharp.MultiTenancy;

namespace OSharp.Entity
{
    /// <summary>
    /// 租户数据库上下文
    /// </summary>
    public class TenantDbContext : DbContextBase
    {
        /// <summary>
        /// 初始化一个<see cref="TenantDbContext"/>类型的新实例
        /// </summary>
        public TenantDbContext(DbContextOptions<TenantDbContext> options, IServiceProvider serviceProvider)
            : base(options, serviceProvider)
        {
        }
    }
}
C# 复制代码
//TenantEntityConfiguration
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using OSharp.Entity;
using OSharp.MultiTenancy;
using System;

namespace Liuliu.Demo.EntityConfiguration
{
    /// <summary>
    /// 租户实体类型配置
    /// </summary>
    public partial class TenantEntityConfiguration : EntityTypeConfigurationBase<TenantEntity, Guid>
    {
        public override Type DbContextType { get; } = typeof(TenantDbContext);

        /// <summary>
        /// 重写以实现实体类型各个属性的数据库配置
        /// </summary>
        /// <param name="builder">实体类型构建器</param>
        public override void Configure(EntityTypeBuilder<TenantEntity> builder)
        {
            // 配置表名
            builder.ToTable("Tenants");

            // 配置属性
            builder.Property(m => m.TenantId)
                .IsRequired()
                .HasMaxLength(50)
                .HasComment("租户ID");

            builder.Property(m => m.Name)
                .IsRequired()
                .HasMaxLength(100)
                .HasComment("租户名称");

            builder.Property(m => m.Host)
                .IsRequired()
                .HasMaxLength(100)
                .HasComment("租户主机");

            builder.Property(m => m.ConnectionString)
                .HasMaxLength(1000)
                .HasComment("连接字符串");

            builder.Property(m => m.IsEnabled)
                .IsRequired()
                .HasDefaultValue(true)
                .HasComment("是否启用");

            builder.Property(m => m.CreatedTime)
                .IsRequired()
                .HasComment("创建时间");

            builder.Property(m => m.UpdatedTime)
                .HasComment("更新时间");

            // 配置索引
            builder.HasIndex(m => m.TenantId)
                .IsUnique()
                .HasDatabaseName("IX_Tenants_TenantId");

            builder.HasIndex(m => m.Host)
                .HasDatabaseName("IX_Tenants_Host");

            EntityConfigurationAppend(builder);
        }

        /// <summary>
        /// 额外的数据映射
        /// </summary>
        partial void EntityConfigurationAppend(EntityTypeBuilder<TenantEntity> builder);
    }
} 

定义租户提供者

C# 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OSharp.MultiTenancy
{
    /// <summary>
    /// 定义租户提供者
    /// </summary>
    public interface ITenantProvider
    {
        /// <summary>
        /// 获取当前租户信息
        /// </summary>
        /// <returns>租户信息</returns>
        TenantInfo GetCurrentTenant();

        /// <summary>
        /// 异步获取当前租户信息
        /// </summary>
        /// <returns>租户信息</returns>
        Task<TenantInfo> GetCurrentTenantAsync();

        /// <summary>
        /// 根据租户标识获取租户信息
        /// </summary>
        /// <param name="identifier">租户标识</param>
        /// <returns>租户信息</returns>
        TenantInfo GetTenant(string identifier);

        /// <summary>
        /// 异步根据租户标识获取租户信息
        /// </summary>
        /// <param name="identifier">租户标识</param>
        /// <returns>租户信息</returns>
        Task<TenantInfo> GetTenantAsync(string identifier);
    }
}
C# 复制代码
//HttpTenantProvider
using OSharp.Dependency;
using OSharp.MultiTenancy;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Liuliu.Demo.Web.Startups
{
    /// <summary>
    /// 基于HTTP请求的租户提供者实现,支持多种方式识别租户
    /// </summary>
    public class HttpTenantProvider : ITenantProvider
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IConfiguration _configuration;
        private readonly ILogger<HttpTenantProvider> _logger;
        private readonly ITenantAccessor _tenantAccessor; // 添加租户访问器
        private readonly ITenantStore _tenantStore;  // 使用 ITenantStore

        // 租户识别方式配置
        private readonly TenantResolveOptions _resolveOptions;

        public HttpTenantProvider(
            IHttpContextAccessor httpContextAccessor,
            IConfiguration configuration,
            ILogger<HttpTenantProvider> logger,
            ITenantStore tenantStore,
            ITenantAccessor tenantAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
            _configuration = configuration;
            _logger = logger;
            _tenantAccessor = tenantAccessor;
            _tenantStore = tenantStore;

            // 初始化租户识别方式配置
            _resolveOptions = new TenantResolveOptions();
            ConfigureTenantResolveOptions();
        }

        /// <summary>
        /// 获取当前租户信息
        /// </summary>
        public async Task<TenantInfo> GetCurrentTenantAsync()
        {
            // 首先检查租户访问器中是否已有租户信息
            if (_tenantAccessor.CurrentTenant != null)
            {
                return _tenantAccessor.CurrentTenant;
            }

            HttpContext httpContext = _httpContextAccessor.HttpContext;
            if (httpContext == null)
            {
                return null;
            }

            // 尝试使用多种方式识别租户
            TenantInfo tenant = null;

            // 按照优先级顺序尝试不同的租户识别方式
            foreach (var resolver in _resolveOptions.Resolvers.OrderBy(r => r.Priority))
            {
                tenant = await resolver.ResolveTenantAsync(httpContext, _tenantStore);
                if (tenant != null)
                {
                    // 将解析到的租户设置到租户访问器中
                    _tenantAccessor.CurrentTenant = tenant;
                    return tenant;
                }
            }

            return tenant;
        }

        /// <summary>
        /// 根据租户标识获取租户信息
        /// </summary>
        public TenantInfo GetTenant(string identifier)
        {
            if (string.IsNullOrEmpty(identifier))
            {
                return null;
            }

            return GetTenantAsync(identifier).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 异步根据租户标识获取租户信息
        /// </summary>
        public async Task<TenantInfo> GetTenantAsync(string identifier)
        {
            if (string.IsNullOrEmpty(identifier))
            {
                return null;
            }

            TenantInfo tenant = await _tenantStore.GetTenantAsync(identifier);
            return tenant?.IsEnabled == true ? tenant : null;
        }

        /// <summary>
        /// 配置租户识别方式
        /// </summary>
        private void ConfigureTenantResolveOptions()
        {
            // 从配置中读取租户识别方式配置
            var resolveSection = _configuration.GetSection("MultiTenancy:TenantResolve");
            
            // 添加域名解析器(默认启用,优先级最高)
            bool enableDomain = resolveSection.GetValue<bool>("EnableDomain", true);
            if (enableDomain)
            {
                _resolveOptions.AddResolver(new DomainTenantResolver(), 100);
            }

            // 添加请求头解析器
            bool enableHeader = resolveSection.GetValue<bool>("EnableHeader", false);
            string headerName = resolveSection.GetValue<string>("HeaderName", "X-Tenant");
            if (enableHeader)
            {
                _resolveOptions.AddResolver(new HeaderTenantResolver(headerName), 200);
            }

            // 添加查询参数解析器
            bool enableQueryString = resolveSection.GetValue<bool>("EnableQueryString", false);
            string queryStringName = resolveSection.GetValue<string>("QueryStringName", "tenant");
            if (enableQueryString)
            {
                _resolveOptions.AddResolver(new QueryStringTenantResolver(queryStringName), 300);
            }

            // 添加Cookie解析器
            bool enableCookie = resolveSection.GetValue<bool>("EnableCookie", false);
            string cookieName = resolveSection.GetValue<string>("CookieName", "tenant");
            if (enableCookie)
            {
                _resolveOptions.AddResolver(new CookieTenantResolver(cookieName), 400);
            }

            // 添加Claims解析器
            bool enableClaim = resolveSection.GetValue<bool>("EnableClaim", false);
            string claimType = resolveSection.GetValue<string>("ClaimType", "tenant");
            if (enableClaim)
            {
                _resolveOptions.AddResolver(new ClaimTenantResolver(claimType), 500);
            }

            // 添加路由解析器
            bool enableRoute = resolveSection.GetValue<bool>("EnableRoute", false);
            string routeParamName = resolveSection.GetValue<string>("RouteParamName", "tenant");
            if (enableRoute)
            {
                _resolveOptions.AddResolver(new RouteTenantResolver(routeParamName), 600);
            }

            _logger.LogInformation("已配置租户识别方式: Domain={0}, Header={1}, QueryString={2}, Cookie={3}, Claim={4}, Route={5}",
                enableDomain, enableHeader, enableQueryString, enableCookie, enableClaim, enableRoute);
        }

        public TenantInfo GetCurrentTenant()
        {
            return GetCurrentTenantAsync().Result;
        }
    }

    /// <summary>
    /// 租户识别选项
    /// </summary>
    public class TenantResolveOptions
    {
        public List<ITenantResolver> Resolvers { get; } = new List<ITenantResolver>();

        public void AddResolver(ITenantResolver resolver, int priority)
        {
            resolver.Priority = priority;
            Resolvers.Add(resolver);
        }
    }

    /// <summary>
    /// 租户解析器接口
    /// </summary>
    public interface ITenantResolver
    {
        /// <summary>
        /// 优先级,数值越小优先级越高
        /// </summary>
        int Priority { get; set; }

        /// <summary>
        /// 解析租户
        /// </summary>
        Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore);
    }

    /// <summary>
    /// 基于域名的租户解析器
    /// </summary>
    public class DomainTenantResolver : ITenantResolver
    {
        public int Priority { get; set; }

        public async Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore)
        {
            string host = context.Request.Host.Host.ToLower();
            if (string.IsNullOrEmpty(host))
            {
                return null;
            }

            // 获取所有租户并查找匹配的租户
            var tenants = await tenantStore.GetAllTenantsAsync();
            return tenants.FirstOrDefault(t =>
                t.IsEnabled &&
                (t.Host.Equals(host, StringComparison.OrdinalIgnoreCase) ||
                 host.EndsWith("." + t.Host, StringComparison.OrdinalIgnoreCase)));
        }
    }

    /// <summary>
    /// 基于请求头的租户解析器
    /// </summary>
    public class HeaderTenantResolver : ITenantResolver
    {
        private readonly string _headerName;

        public HeaderTenantResolver(string headerName)
        {
            _headerName = headerName;
        }

        public int Priority { get; set; }

        public async Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore)
        {
            if (!context.Request.Headers.TryGetValue(_headerName, out var values) || values.Count == 0)
            {
                return null;
            }

            string tenantId = values.First();
            if (string.IsNullOrEmpty(tenantId))
            {
                return null;
            }

            return await tenantStore.GetTenantAsync(tenantId);
        }
    }

    /// <summary>
    /// 基于查询参数的租户解析器
    /// </summary>
    public class QueryStringTenantResolver : ITenantResolver
    {
        private readonly string _paramName;

        public QueryStringTenantResolver(string paramName)
        {
            _paramName = paramName;
        }

        public int Priority { get; set; }

        public async Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore)
        {
            if (!context.Request.Query.TryGetValue(_paramName, out var values) || values.Count == 0)
            {
                return null;
            }

            string tenantId = values.First();
            if (string.IsNullOrEmpty(tenantId))
            {
                return null;
            }

            return await tenantStore.GetTenantAsync(tenantId);
        }
    }

    /// <summary>
    /// 基于Cookie的租户解析器
    /// </summary>
    public class CookieTenantResolver : ITenantResolver
    {
        private readonly string _cookieName;

        public CookieTenantResolver(string cookieName)
        {
            _cookieName = cookieName;
        }

        public int Priority { get; set; }

        public async Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore)
        {
            if (!context.Request.Cookies.TryGetValue(_cookieName, out string tenantId) || string.IsNullOrEmpty(tenantId))
            {
                return null;
            }

            return await tenantStore.GetTenantAsync(tenantId);
        }
    }

    /// <summary>
    /// 基于Claims的租户解析器
    /// </summary>
    public class ClaimTenantResolver : ITenantResolver
    {
        private readonly string _claimType;

        public ClaimTenantResolver(string claimType)
        {
            _claimType = claimType;
        }

        public int Priority { get; set; }

        public async Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore)
        {
            if (!context.User.Identity.IsAuthenticated)
            {
                return null;
            }

            var claim = context.User.Claims.FirstOrDefault(c => c.Type == _claimType);
            if (claim == null || string.IsNullOrEmpty(claim.Value))
            {
                return null;
            }
            var tenantId = claim.Value;
            return await tenantStore.GetTenantAsync(tenantId);
        }
    }

    /// <summary>
    /// 基于路由参数的租户解析器
    /// </summary>
    public class RouteTenantResolver : ITenantResolver
    {
        private readonly string _routeParamName;

        public RouteTenantResolver(string routeParamName)
        {
            _routeParamName = routeParamName;
        }

        public int Priority { get; set; }

        public async Task<TenantInfo> ResolveTenantAsync(HttpContext context, ITenantStore tenantStore)
        {
            if (!context.Request.RouteValues.TryGetValue(_routeParamName, out var value) || value == null)
            {
                return null;
            }

            string tenantId = value.ToString();
            if (string.IsNullOrEmpty(tenantId))
            {
                return null;
            }

            return await tenantStore.GetTenantAsync(tenantId);
        }
    }
}

定义租户中间件

C# 复制代码
using Microsoft.AspNetCore.Http;
using OSharp.MultiTenancy;
using System;
using System.Threading.Tasks;

namespace Liuliu.Demo.Web.Startups
{
    /// <summary>
    /// 多租户中间件,用于在请求处理过程中设置当前租户
    /// </summary>
    public class TenantMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<TenantMiddleware> _logger;

        public TenantMiddleware(
            RequestDelegate next,
            ILogger<TenantMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context, ITenantProvider tenantProvider, ITenantAccessor tenantAccessor)
        {
            try
            {
                // 解析当前租户
                TenantInfo tenant = await tenantProvider.GetCurrentTenantAsync();
                
                // 设置当前租户到访问器中
                if (tenant != null)
                {
                    tenantAccessor.CurrentTenant = tenant;
                    _logger.LogDebug("已设置当前租户: {TenantId}, {Name}", tenant.TenantId, tenant.Name);
                    
                    // 可以在这里添加租户相关的请求头或其他信息
                    context.Items["CurrentTenant"] = tenant;
                }
                else
                {
                    _logger.LogDebug("未识别到租户信息");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "处理租户信息时发生错误");
            }

            // 继续处理请求
            await _next(context);
        }
    }
}

数据库迁移器TenantDatabaseMigrator

C# 复制代码
using OSharp.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OSharp.Entity;
using Microsoft.EntityFrameworkCore;
using OSharp.Authorization.Modules;
using OSharp.Authorization.Functions;
using OSharp.AspNetCore.Mvc;
using OSharp.Authorization;
using OSharp.Authorization.EntityInfos;

namespace Liuliu.Demo.Web.Startups
{
    public class TenantDatabaseMigrator
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly ITenantStore _tenantStore;
        private readonly ILogger<TenantDatabaseMigrator> _logger;

        public TenantDatabaseMigrator(
            IServiceProvider serviceProvider,
            ITenantStore tenantStore,
            ILogger<TenantDatabaseMigrator> logger)
        {
            _serviceProvider = serviceProvider;
            _tenantStore = tenantStore;
            _logger = logger;
        }

        /// <summary>
        /// 迁移所有租户数据库
        /// </summary>
        public async Task MigrateAllTenantsAsync()
        {
            // 获取所有租户
            var tenants = await _tenantStore.GetAllTenantsAsync();

            foreach (var tenant in tenants)
            {
                try
                {
                    await MigrateTenantDatabaseAsync(tenant);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "迁移租户 {TenantId} 的数据库时出错", tenant.TenantId);
                }
            }
        }

        /// <summary>
        /// 迁移指定租户的数据库
        /// </summary>
        public async Task MigrateTenantDatabaseAsync(TenantInfo tenant)
        {
            if (tenant == null || !tenant.IsEnabled)
            {
                return;
            }

            _logger.LogInformation("开始迁移租户 {TenantId} 的数据库", tenant.TenantId);

            // 创建一个新的作用域,以便我们可以使用租户特定的服务
            using (var scope = _serviceProvider.CreateScope())
            {
                // 设置当前租户
                var tenantAccessor = scope.ServiceProvider.GetRequiredService<ITenantAccessor>();
                tenantAccessor.CurrentTenant = tenant;

                // 获取 DbContext 并执行迁移
                var dbContext = new DesignTimeDefaultDbContextFactory(scope.ServiceProvider).CreateDbContext(new string[0]);//scope.ServiceProvider.GetRequiredService<DefaultDbContext>();
                ILogger logger = _serviceProvider.GetLogger(GetType());
                dbContext.CheckAndMigration(logger);
            }
            InitializeSeedDataAsync(tenant);

            InitFrameWorkData(tenant);
        }
        /// <summary>
        /// 初始化种子数据
        /// </summary>
        private void InitializeSeedDataAsync(TenantInfo tenant)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                //初始化种子数据,只初始化当前上下文的种子数据
                IEntityManager entityManager = scope.ServiceProvider.GetRequiredService<IEntityManager>();
                Type[] entityTypes = entityManager.GetEntityRegisters(typeof(DefaultDbContext)).Select(m => m.EntityType).Distinct().ToArray();
                IEnumerable<ISeedDataInitializer> seedDataInitializers = scope.ServiceProvider.GetServices<ISeedDataInitializer>()
                    .Where(m => entityTypes.Contains(m.EntityType)).OrderBy(m => m.Order);
                try
                {
                    ITenantAccessor tenantAccessor = scope.ServiceProvider.GetRequiredService<ITenantAccessor>();
                    tenantAccessor.CurrentTenant = tenant;
                    foreach (ISeedDataInitializer initializer in seedDataInitializers)
                    {
                        initializer.Initialize();
                    }

                    var csp = scope.ServiceProvider.GetRequiredService<IConnectionStringProvider>();
                    var str = csp.GetConnectionString(typeof(DefaultDbContext));
                    _logger.LogInformation("IConnectionStringProvider 链接:" + str);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "初始化种子数据时出错" + ex.Message);
                }
            }
        }

        private void InitFrameWorkData(TenantInfo tenant)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                ITenantAccessor tenantAccessor = scope.ServiceProvider.GetRequiredService<ITenantAccessor>();
                tenantAccessor.CurrentTenant = tenant;

                IFunctionHandler functionHandler = scope.ServiceProvider.GetServices<IFunctionHandler>().FirstOrDefault(m => m.GetType() == typeof(MvcFunctionHandler));
                if (functionHandler != null)
                {
                    functionHandler.Initialize();
                }

                IModuleHandler moduleHandler = scope.ServiceProvider.GetRequiredService<IModuleHandler>();
                moduleHandler.Initialize();

                //IFunctionAuthCache functionAuthCache = scope.ServiceProvider.GetRequiredService<IFunctionAuthCache>();
                //functionAuthCache.BuildRoleCaches();

                IEntityInfoHandler entityInfoHandler = scope.ServiceProvider.GetRequiredService<IEntityInfoHandler>();
                entityInfoHandler.Initialize();

                
            }
        }
    }
}

配置文件

json 复制代码
{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information",
      "OSharp": "Debug",
      "Liuliu": "Debug"
    }
  },
  "ConnectionStrings": {
    "Tenant": "Server=(localdb)\\mssqllocaldb;Database=MasterDb;Trusted_Connection=True;MultipleActiveResultSets=true",
    "Default": "Server=(localdb)\\mssqllocaldb;Database=DefaultDb;Trusted_Connection=True;MultipleActiveResultSets=true",
    "tenant0": {
      "Default": "Server=(localdb)\\mssqllocaldb;Database=Tenant0Db;Trusted_Connection=True;MultipleActiveResultSets=true"
    },
    "tenant1": {
      "Default": "Server=(localdb)\\mssqllocaldb;Database=Tenant1Db;Trusted_Connection=True;MultipleActiveResultSets=true"
    },
    "tenant2": {
      "Default": "Server=(localdb)\\mssqllocaldb;Database=Tenant2Db;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  },
  "MultiTenancy": {
    "TenantResolve": {
      "EnableDomain": true,
      "EnableHeader": true,
      "HeaderName": "X-Tenant",
      "EnableQueryString": true,
      "QueryStringName": "tenant",
      "EnableCookie": true,
      "CookieName": "tenant",
      "EnableClaim": true,
      "ClaimType": "tenant",
      "EnableRoute": true,
      "RouteParamName": "tenant"
    }
  },
  "Tenants": {
    "tenant0": {
      "Name": "租户0",
      "Host": "localhost",
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Tenant0Db;Trusted_Connection=True;MultipleActiveResultSets=true",
      "IsEnabled": true
    },
    "tenant1": {
      "Name": "租户1",
      "Host": "tenant1",
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Tenant1Db;Trusted_Connection=True;MultipleActiveResultSets=true",
      "IsEnabled": true
    },
    "tenant2": {
      "Name": "租户2",
      "Host": "tenant2",
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Tenant2Db;Trusted_Connection=True;MultipleActiveResultSets=true",
      "IsEnabled": true
    }
  },
  "OSharp": {
    "DbContexts": {
      "SqlServer": {
        "DbContextTypeName": "OSharp.Entity.DefaultDbContext,OSharp.EntityFrameworkCore",
        "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=osharpns-dev01;Trusted_Connection=True;MultipleActiveResultSets=true",
        "DatabaseType": "SqlServer",
        "LazyLoadingProxiesEnabled": true,
        "AuditEntityEnabled": true,
        "AutoMigrationEnabled": true
      },
      "Tenant": {
        "DbContextTypeName": "OSharp.Entity.TenantDbContext,OSharp.EntityFrameworkCore",
        "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=osharpns-dev02;Trusted_Connection=True;MultipleActiveResultSets=true",
        "DatabaseType": "SqlServer",
        "LazyLoadingProxiesEnabled": true,
        "AuditEntityEnabled": true,
        "AutoMigrationEnabled": true
      }
      //,
      //"MySql": {
      //  "DbContextTypeName": "OSharp.Entity.DefaultDbContext,OSharp.EntityFrameworkCore",
      //  "ConnectionString": "Server=127.0.0.1;UserId=root;Password=abc123456;Database=osharpns-dev3;charset='utf8';Allow User Variables=True",
      //  "DatabaseType": "MySql",
      //  "LazyLoadingProxiesEnabled": true,
      //  "AuditEntityEnabled": true,
      //  "AutoMigrationEnabled": true
      //}
      //,
      //"Sqlite": {
      //  "DbContextTypeName": "OSharp.Entity.DefaultDbContext,OSharp.EntityFrameworkCore",
      //  "ConnectionString": "data source=osharpns.db",
      //  "DatabaseType": "Sqlite",
      //  "LazyLoadingProxiesEnabled": true,
      //  "AuditEntityEnabled": true,
      //  "AutoMigrationEnabled": true
      //}
      //,
      //"PostgreSql": {
      //  "DbContextTypeName": "OSharp.Entity.DefaultDbContext,OSharp.EntityFrameworkCore",
      //  "ConnectionString": "User ID=postgres;Password=abc123456;Host=localhost;Port=5432;Database=osharpns.demo-dev3",
      //  "DatabaseType": "PostgreSql",
      //  "LazyLoadingProxiesEnabled": true,
      //  "AuditEntityEnabled": true,
      //  "AutoMigrationEnabled": true
      //}
    },
    "OAuth2": {
      //"QQ": {
      //  "ClientId": "你的QQ互联项目AppId",
      //  "ClientSecret": "你的QQ互联项目AppKey",
      //  "Enabled": false
      //},
      //"Microsoft": {
      //  "ClientId": "你的微软项目ClientId",
      //  "ClientSecret": "你的微软项目ClientSecret",
      //  "Enabled": false
      //},
      //"GitHub": {
      //  "ClientId": "你的微软项目ClientId",
      //  "ClientSecret": "你的微软项目ClientSecret",
      //  "Enabled": false
      //}
    },
    "HttpEncrypt": {
      "HostPrivateKey": "<RSAKeyValue><Modulus>npBAH/wQ+CzWz0cNvt7TRroWzE4dF5TvQjQqoEa+7PutPPPsLYkHlCXlDWqv0gwGDc/vg9mzaxFFWFFKi+hMjwh1dkDIa9GVm3umB8Ris1j2yNqIvOEXDPpwbnCgqwP8HsnOwRG0Klqc3qjZlaaex0cv8XY/9v2l6qYkd9J0imtBLL+22bfPW+a/qtQfvVkNAKNm6gjLLFwkXBnw3WnBNvmpqR70fe4NtIX3gIY5uWQcD5pdBmpG1uwzCatiA2b2Gso+tr/CE5nbZ7BCofIGu4Q1JSpQTzbVnLq0+e3z3ysLNZbbXHxkcTtihKOSHhkHGnSUnJZCKHaoAlNT1tAP2Q==</Modulus><Exponent>AQAB</Exponent><P>0Xm6GIcZy2C6jj0pfhFvQfOFzOGgocXLyOJAlkZ2ULk033Xx/LdDogHzte2sxxSzuTApoJHQuCmuBIKJI74SkjAAAcRNwrhbuzLB5ulAUlTINBUHixn+PFd7m1nug0PfcTbAnapUx5n+5WTR/9zbnnfsyHXRBFumzhzByF85ibc=</P><Q>wcfJ5NkRgGDUAE8sL1Gcf5TQAJ2dK4/coHal38S2v+HzkuGGFDwICfNm/q9IzIDa72MhjBlbE7zBsRmHy2wJL/bVB1xxpfWXPZ2sHzkWeuLBEY5V1k0MaUxjFEhfgLi439IFFEPczFl3ndpNR6DYBc7Jcou0J90w9rMq2Wljcu8=</Q><DP>kHnFaX9cwhHv+YSjpoi91J3yTbHciVcTy3SJGVx15A0pM2p0wVlg808nWPYZcaGMp5BZVZ7cdviARioGDjndMyiaCJ3tB/0Bf6ZtaCa+L0q8XneWoVEHMXUhEq+/OpfId5xM0zGUkapbzLlxwWgBrVWHYWcpBzlzXbslyF4tIBc=</DP><DQ>WQbNxZrIhJ93prC5DwBCkwauTSocVDAi34HDETwR7bQEMH32GIO/+bpenjGvk2y7qPF1LyVTB41Xu2KMVbPLwMJ4+onJGMLs+fzfX/TdVBWrN8KZwvvg8NuMRXw+jCfRn9qgRMAsx6Fu6BGsIXVO6dQoDr0KRqpDXYPQ8tONQfc=</DQ><InverseQ>wo7bSu+OlrXYWC6EpzmFoqHrCCzVQ/Vbf/0HsMFl5CFB8k3LJjngyFDkUi/5NNNJ/BZv0oE3pk6XyHEXGlk/MZV4FCO/YrF4DSZ++ecD0/aVQDGfCNDU0HFLstBEQDhmRTQZEkkLHN8O+zjFKDzujBMY4DYZcXpFbr0srOhiDv4=</InverseQ><D>eOZgHoMhpTj7KNxyfKCF0528GFdPE1X6AC6qeb63gRZ9BsatxCKBZtJmDY7VNID6dLmhVJU/mll22FpTTs324fB/L4VBzAPn4L+s3qs83qbstET8kZfG7Zxe8bZqqzrEl+0j+k43Yzvn9FYmYVbEJgn/YkrZhsfsJDg+AiazuX3OoFfPmgArsBmiii+7nWLmMnpoiebxGS29YDl2YqrntkcVuOpZALHDefHQdJcsNpJgFd6Dbm77ajZhpJppC4mtuUu0agUmJL3Uc6SLYHjIp/tDd2L8noZipYuNfzxxuf5KejWZM4FT6zGbU/QEurvTWMQAqp2ibixl614mUXFcKQ==</D></RSAKeyValue>",
      "HostPublicKey": "<RSAKeyValue><Modulus>npBAH/wQ+CzWz0cNvt7TRroWzE4dF5TvQjQqoEa+7PutPPPsLYkHlCXlDWqv0gwGDc/vg9mzaxFFWFFKi+hMjwh1dkDIa9GVm3umB8Ris1j2yNqIvOEXDPpwbnCgqwP8HsnOwRG0Klqc3qjZlaaex0cv8XY/9v2l6qYkd9J0imtBLL+22bfPW+a/qtQfvVkNAKNm6gjLLFwkXBnw3WnBNvmpqR70fe4NtIX3gIY5uWQcD5pdBmpG1uwzCatiA2b2Gso+tr/CE5nbZ7BCofIGu4Q1JSpQTzbVnLq0+e3z3ysLNZbbXHxkcTtihKOSHhkHGnSUnJZCKHaoAlNT1tAP2Q==</Modulus><Exponent>AQAB</Exponent><P></P><Q></Q><DP></DP><DQ></DQ><InverseQ></InverseQ><D></D></RSAKeyValue>",
      "Enabled": false
    },
    "MailSender": {
      "Host": "smtp.mxhichina.com",
      "Port": 587,
      "EnableSsl": true,
      "DisplayName": "OSharp邮件发送",
      "UserName": "osharpsender@66soft.net",
      "Password": "OSharp147963"
    },
    "Jwt": {
      "Issuer": "osharp identity",
      "Audience": "osharp angular demo",
      "Secret": "{8619F7C3-B53C-4B85-99F0-983D351ECD82}",
      "AccessExpireMins": 5,
      "RefreshExpireMins": 10080, // 7天
      "IsRefreshAbsoluteExpired": false,
      "Enabled": true
    },
    //"Cookie": {
    //  "Enabled": false
    //},
    "Cors": {
      "PolicyName": "MyCors",
      "AllowAnyHeader": true,
      "WithMethods": [ "POST", "PUT", "DELETE" ],
      "WithOrigins": [ "http://example.com" ],
      "Enabled": true
    },
    "Redis": {
      "Configuration": "localhost",
      "InstanceName": "OSharpDemo:"
    },
    "Swagger": {
      "Endpoints": [
        {
          "Title": "框架API",
          "Version": "v1",
          "Url": "/swagger/v1/swagger.json"
        },
        {
          "Title": "业务API",
          "Version": "buss",
          "Url": "/swagger/buss/swagger.json"
        }
      ],
      "RoutePrefix": "swagger",
      "IsHideSchemas": true,
      "MiniProfiler": false,
      "Enabled": true
    },
    "Hangfire": {
      "WorkerCount": 20,
      "StorageConnectionString": "Server=.;Database=osharpns.hangfire-dev;User Id=sa;Password=Abc123456!;MultipleActiveResultSets=true",
      "DashboardUrl": "/hangfire",
      "Roles": ""
    }
  }
}

租户信息存文件的配置文件

json 复制代码
//tenants.json
[
  {
    "tenantId": "tenant0",
    "name": "租户0",
    "host": "localhost",
    "connectionString": "Server=(localdb)\\mssqllocaldb;Database=Tenant0Db;Trusted_Connection=True;MultipleActiveResultSets=true",
    "isEnabled": true
  },
  {
    "tenantId": "tenant1",
    "name": "租户1",
    "host": "tenant1",
    "connectionString": "Server=(localdb)\\mssqllocaldb;Database=Tenant1Db;Trusted_Connection=True;MultipleActiveResultSets=true",
    "isEnabled": true
  },
  {
    "tenantId": "tenant2",
    "name": "租户2",
    "host": "tenant2",
    "connectionString": "Server=(localdb)\\mssqllocaldb;Database=Tenant2Db;Trusted_Connection=True;MultipleActiveResultSets=true",
    "isEnabled": true
  }
]

全局缓存接口

C# 复制代码
using System.Threading.Tasks;

namespace OSharp.Caching
{
    /// <summary>
    /// 全局缓存键生成器接口
    /// </summary>
    public interface IGlobalCacheKeyGenerator
    {
        /// <summary>
        /// 获取全局缓存键
        /// </summary>
        /// <param name="args">参数</param>
        /// <returns>缓存键</returns>
        string GetGlobalKey(params object[] args);

        /// <summary>
        /// 异步获取全局缓存键
        /// </summary>
        /// <param name="args">参数</param>
        /// <returns>缓存键</returns>
        Task<string> GetGlobalKeyAsync(params object[] args);
    }
}

定义缓存服务功能接口

C# 复制代码
// -----------------------------------------------------------------------
//  <copyright file="ICacheService.cs" company="OSharp开源团队">
//      Copyright (c) 2014-2018 OSharp. All rights reserved.
//  </copyright>
//  <site>http://www.osharp.org</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2018-12-19 18:07</last-date>
// -----------------------------------------------------------------------

namespace OSharp.Caching;

/// <summary>
/// 定义缓存服务功能
/// </summary>
public interface ICacheService
{
    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据筛选表达式</param>
    /// <param name="pageCondition">分页条件</param>
    /// <param name="selector">数据投影表达式</param>
    /// <param name="cacheSeconds">缓存时间</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    PageResult<TResult> ToPageCache<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        PageCondition pageCondition,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据筛选表达式</param>
    /// <param name="pageCondition">分页条件</param>
    /// <param name="selector">数据投影表达式</param>
    /// <param name="function">当前功能信息</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    PageResult<TResult> ToPageCache<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        PageCondition pageCondition,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    List<TSource> ToCacheList<TSource>(IQueryable<TSource> source, int cacheSeconds = 60, params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    TSource[] ToCacheArray<TSource>(IQueryable<TSource> source, int cacheSeconds = 60, params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    List<TSource> ToCacheList<TSource>(IQueryable<TSource> source, IFunction function, params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    TSource[] ToCacheArray<TSource>(IQueryable<TSource> source, IFunction function, params object[] keyParams);

    #region OutputDto

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <typeparam name="TOutputDto">分页数据类型</typeparam>
    /// <param name="source">要查询的数据集</param>
    /// <param name="predicate">查询条件谓语表达式</param>
    /// <param name="pageCondition">分页查询条件</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询的分页结果</returns>
    PageResult<TOutputDto> ToPageCache<TEntity, TOutputDto>(IQueryable<TEntity> source,
        Expression<Func<TEntity, bool>> predicate,
        PageCondition pageCondition,
        int cacheSeconds = 60,
        params object[] keyParams)
        where TOutputDto : IOutputDto;

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <typeparam name="TOutputDto">分页数据类型</typeparam>
    /// <param name="source">要查询的数据集</param>
    /// <param name="predicate">查询条件谓语表达式</param>
    /// <param name="pageCondition">分页查询条件</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询的分页结果</returns>
    PageResult<TOutputDto> ToPageCache<TEntity, TOutputDto>(IQueryable<TEntity> source,
        Expression<Func<TEntity, bool>> predicate,
        PageCondition pageCondition,
        IFunction function,
        params object[] keyParams)
        where TOutputDto : IOutputDto;

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source,
        int cacheSeconds = 60,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source,
        IFunction function,
        params object[] keyParams);

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source,
        IFunction function,
        params object[] keyParams);

    #endregion

    /// <summary>
    /// 获取或添加全局缓存,不考虑租户
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="factory">缓存数据获取工厂</param>
    /// <param name="expiration">过期时间</param>
    /// <returns>缓存数据</returns>
    T GetOrAddGlobal<T>(string key, Func<T> factory, TimeSpan? expiration = null);

    /// <summary>
    /// 异步获取或添加全局缓存,不考虑租户
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="factory">缓存数据获取工厂</param>
    /// <param name="expiration">过期时间</param>
    /// <returns>缓存数据</returns>
    Task<T> GetOrAddGlobalAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null);

    /// <summary>
    /// 移除全局缓存,不考虑租户
    /// </summary>
    /// <param name="key">缓存键</param>
    void RemoveGlobal(string key);

    /// <summary>
    /// 异步移除全局缓存,不考虑租户
    /// </summary>
    /// <param name="key">缓存键</param>
    Task RemoveGlobalAsync(string key);

    // 在 ICacheService 接口中添加以下方法

    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    /// <param name="expiration">过期时间</param>
    void Set<T>(string key, T value, TimeSpan? expiration = null);

    /// <summary>
    /// 异步设置缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    /// <param name="expiration">过期时间</param>
    /// <returns>异步任务</returns>
    Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);

    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns>缓存数据</returns>
    T Get<T>(string key);

    /// <summary>
    /// 异步获取缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns>缓存数据</returns>
    Task<T> GetAsync<T>(string key);
}

缓存服务实现

C# 复制代码
// -----------------------------------------------------------------------
//  <copyright file="CacheService.cs" company="OSharp开源团队">
//      Copyright (c) 2014-2018 OSharp. All rights reserved.
//  </copyright>
//  <site>http://www.osharp.org</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2018-12-19 19:10</last-date>
// -----------------------------------------------------------------------

using System.Text.Json;

namespace OSharp.Caching;

/// <summary>
/// 缓存服务实现
/// </summary>
public class CacheService : ICacheService
{
    private readonly IDistributedCache _cache;
    private readonly ICacheKeyGenerator _keyGenerator;
    private readonly IGlobalCacheKeyGenerator _globalKeyGenerator;
    private readonly ILogger<CacheService> _logger;
    private readonly IServiceProvider _serviceProvider;

    /// <summary>
    /// 初始化一个<see cref="CacheService"/>类型的新实例
    /// </summary>
    public CacheService(
        IDistributedCache cache,
        ICacheKeyGenerator keyGenerator,
        ILogger<CacheService> logger,
        IServiceProvider serviceProvider)
    {
        _cache = cache;
        _keyGenerator = keyGenerator;
        _globalKeyGenerator = keyGenerator as IGlobalCacheKeyGenerator;
        _logger = logger;
        _serviceProvider = serviceProvider;
    }

    #region Implementation of ICacheService

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据筛选表达式</param>
    /// <param name="pageCondition">分页条件</param>
    /// <param name="selector">数据投影表达式</param>
    /// <param name="cacheSeconds">缓存时间</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual PageResult<TResult> ToPageCache<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        PageCondition pageCondition,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        string key = GetKey(source, predicate, pageCondition, selector, keyParams);
        return _cache.Get(key, () => source.ToPage(predicate, pageCondition, selector), cacheSeconds);
    }

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据筛选表达式</param>
    /// <param name="pageCondition">分页条件</param>
    /// <param name="selector">数据投影表达式</param>
    /// <param name="function">当前功能信息</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual PageResult<TResult> ToPageCache<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        PageCondition pageCondition,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams)
    {
        string key = GetKey(source, predicate, pageCondition, selector, keyParams);
        return _cache.Get(key, () => source.ToPage(predicate, pageCondition, selector), function);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        return ToCacheList(source.Where(predicate), selector, cacheSeconds, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        return ToCacheArray(source.Where(predicate), selector, cacheSeconds, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams)
    {
        return ToCacheList(source.Where(predicate), selector, function, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams)
    {
        return ToCacheArray(source.Where(predicate), selector, function, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        string key = GetKey(source, selector, keyParams);
        return _cache.Get(key, () => source.Select(selector).ToList(), cacheSeconds);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        string key = GetKey(source, selector, keyParams);
        return _cache.Get(key, () => source.Select(selector).ToArray(), cacheSeconds);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual List<TResult> ToCacheList<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams)
    {
        string key = GetKey(source, selector, keyParams);
        return _cache.Get(key, () => source.Select(selector).ToList(), function);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TResult">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="selector">数据筛选表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual TResult[] ToCacheArray<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        IFunction function,
        params object[] keyParams)
    {
        string key = GetKey(source, selector, keyParams);
        return _cache.Get(key, () => source.Select(selector).ToArray(), function);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual List<TSource> ToCacheList<TSource>(IQueryable<TSource> source, int cacheSeconds = 60, params object[] keyParams)
    {
        string key = GetKey(source.Expression, keyParams);
        return _cache.Get(key, source.ToList, cacheSeconds);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual TSource[] ToCacheArray<TSource>(IQueryable<TSource> source, int cacheSeconds = 60, params object[] keyParams)
    {
        string key = GetKey(source.Expression, keyParams);
        return _cache.Get(key, source.ToArray, cacheSeconds);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual List<TSource> ToCacheList<TSource>(IQueryable<TSource> source, IFunction function, params object[] keyParams)
    {
        if (function == null || function.CacheExpirationSeconds <= 0)
        {
            return source.ToList();
        }

        string key = GetKey(source.Expression, keyParams);
        return _cache.Get(key, source.ToList, function);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual TSource[] ToCacheArray<TSource>(IQueryable<TSource> source, IFunction function, params object[] keyParams)
    {
        if (function == null || function.CacheExpirationSeconds <= 0)
        {
            return source.ToArray();
        }

        string key = GetKey(source.Expression, keyParams);
        return _cache.Get(key, source.ToArray, function);
    }

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <typeparam name="TOutputDto">分页数据类型</typeparam>
    /// <param name="source">要查询的数据集</param>
    /// <param name="predicate">查询条件谓语表达式</param>
    /// <param name="pageCondition">分页查询条件</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询的分页结果</returns>
    public virtual PageResult<TOutputDto> ToPageCache<TEntity, TOutputDto>(IQueryable<TEntity> source,
        Expression<Func<TEntity, bool>> predicate,
        PageCondition pageCondition,
        int cacheSeconds = 60,
        params object[] keyParams) where TOutputDto : IOutputDto
    {
        string key = GetKey<TEntity, TOutputDto>(source, predicate, pageCondition, keyParams);
        return _cache.Get(key, () => source.ToPage<TEntity, TOutputDto>(predicate, pageCondition), cacheSeconds);
    }

    /// <summary>
    /// 查询分页数据结果,如缓存存在,直接返回,否则从数据源查找分页结果,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <typeparam name="TOutputDto">分页数据类型</typeparam>
    /// <param name="source">要查询的数据集</param>
    /// <param name="predicate">查询条件谓语表达式</param>
    /// <param name="pageCondition">分页查询条件</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询的分页结果</returns>
    public virtual PageResult<TOutputDto> ToPageCache<TEntity, TOutputDto>(IQueryable<TEntity> source,
        Expression<Func<TEntity, bool>> predicate,
        PageCondition pageCondition,
        IFunction function,
        params object[] keyParams) where TOutputDto : IOutputDto
    {
        string key = GetKey<TEntity, TOutputDto>(source, predicate, pageCondition, keyParams);
        return _cache.Get(key, () => source.ToPage<TEntity, TOutputDto>(predicate, pageCondition), function);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        return ToCacheList<TSource, TOutputDto>(source.Where(predicate), cacheSeconds, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="cacheSeconds">缓存时间:秒</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        int cacheSeconds = 60,
        params object[] keyParams)
    {
        return ToCacheArray<TSource, TOutputDto>(source.Where(predicate), cacheSeconds, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        IFunction function,
        params object[] keyParams)
    {
        return ToCacheList<TSource, TOutputDto>(source.Where(predicate), function, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">数据源的项数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">数据源</param>
    /// <param name="predicate">数据查询表达式</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns></returns>
    public virtual TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate,
        IFunction function,
        params object[] keyParams)
    {
        return ToCacheArray<TSource, TOutputDto>(source.Where(predicate), function, keyParams);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source, int cacheSeconds = 60, params object[] keyParams)
    {
        string key = GetKey<TSource, TOutputDto>(source, keyParams);
        return _cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToList(), cacheSeconds);
    }

    /// <summary>
    /// 将结果转换为缓存的数组,如缓存存在,直接返回,否则从数据源查询,并存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="cacheSeconds">缓存的秒数</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source, int cacheSeconds = 60, params object[] keyParams)
    {
        string key = GetKey<TSource, TOutputDto>(source, keyParams);
        return _cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToArray(), cacheSeconds);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual List<TOutputDto> ToCacheList<TSource, TOutputDto>(IQueryable<TSource> source, IFunction function, params object[] keyParams)
    {
        string key = GetKey<TSource, TOutputDto>(source, keyParams);
        return _cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToList(), function);
    }

    /// <summary>
    /// 将结果转换为缓存的列表,如缓存存在,直接返回,否则从数据源查询,并按指定缓存策略存入缓存中再返回
    /// </summary>
    /// <typeparam name="TSource">源数据类型</typeparam>
    /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
    /// <param name="source">查询数据源</param>
    /// <param name="function">缓存策略相关功能</param>
    /// <param name="keyParams">缓存键参数</param>
    /// <returns>查询结果</returns>
    public virtual TOutputDto[] ToCacheArray<TSource, TOutputDto>(IQueryable<TSource> source, IFunction function, params object[] keyParams)
    {
        string key = GetKey<TSource, TOutputDto>(source, keyParams);
        return _cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToArray(), function);
    }

    #endregion

    #region 私有方法

    private string GetKey<TEntity, TResult>(IQueryable<TEntity> source,
        Expression<Func<TEntity, bool>> predicate,
        PageCondition pageCondition,
        Expression<Func<TEntity, TResult>> selector,
        params object[] keyParams)
    {
        source = source.Where(predicate);
        SortCondition[] sortConditions = pageCondition.SortConditions;
        if (sortConditions == null || sortConditions.Length == 0)
        {
            if (typeof(TEntity).IsEntityType())
            {
                source = source.OrderBy("Id");
            }
            else if (typeof(TEntity).IsBaseOn<ICreatedTime>())
            {
                source = source.OrderBy("CreatedTime");
            }
            else
            {
                throw new OsharpException($"类型"{typeof(TEntity)}"未添加默认排序方式");
            }
        }
        else
        {
            int count = 0;
            IOrderedQueryable<TEntity> orderSource = null;
            foreach (SortCondition sortCondition in sortConditions)
            {
                orderSource = count == 0
                    ? CollectionPropertySorter<TEntity>.OrderBy(source, sortCondition.SortField, sortCondition.ListSortDirection)
                    : CollectionPropertySorter<TEntity>.ThenBy(orderSource, sortCondition.SortField, sortCondition.ListSortDirection);
                count++;
            }

            source = orderSource;
        }

        int pageIndex = pageCondition.PageIndex, pageSize = pageCondition.PageSize;
        source = source != null
            ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
            : Enumerable.Empty<TEntity>().AsQueryable();
        IQueryable<TResult> query = source.Select(selector);
        return GetKey(query.Expression, keyParams);
    }

    private string GetKey<TEntity, TOutputDto>(IQueryable<TEntity> source,
        Expression<Func<TEntity, bool>> predicate,
        PageCondition pageCondition,
        params object[] keyParams)
        where TOutputDto : IOutputDto
    {
        source = source.Where(predicate);
        SortCondition[] sortConditions = pageCondition.SortConditions;
        if (sortConditions == null || sortConditions.Length == 0)
        {
            if (typeof(TEntity).IsEntityType())
            {
                source = source.OrderBy("Id");
            }
            else if (typeof(TEntity).IsBaseOn<ICreatedTime>())
            {
                source = source.OrderBy("CreatedTime");
            }
            else
            {
                throw new OsharpException($"类型"{typeof(TEntity)}"未添加默认排序方式");
            }
        }
        else
        {
            int count = 0;
            IOrderedQueryable<TEntity> orderSource = null;
            foreach (SortCondition sortCondition in sortConditions)
            {
                orderSource = count == 0
                    ? CollectionPropertySorter<TEntity>.OrderBy(source, sortCondition.SortField, sortCondition.ListSortDirection)
                    : CollectionPropertySorter<TEntity>.ThenBy(orderSource, sortCondition.SortField, sortCondition.ListSortDirection);
                count++;
            }

            source = orderSource;
        }

        int pageIndex = pageCondition.PageIndex, pageSize = pageCondition.PageSize;
        source = source != null
            ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
            : Enumerable.Empty<TEntity>().AsQueryable();
        IQueryable<TOutputDto> query = source.ToOutput<TEntity, TOutputDto>(true);
        return GetKey(query.Expression, keyParams);
    }

    private string GetKey<TSource, TOutputDto>(IQueryable<TSource> source,
        params object[] keyParams)
    {
        IQueryable<TOutputDto> query = source.ToOutput<TSource, TOutputDto>(true);
        return GetKey(query.Expression, keyParams);
    }

    private string GetKey<TSource, TResult>(IQueryable<TSource> source,
        Expression<Func<TSource, TResult>> selector,
        params object[] keyParams)
    {
        IQueryable<TResult> query = source.Select(selector);
        return GetKey(query.Expression, keyParams);
    }

    private string GetKey(Expression expression, params object[] keyParams)
    {
        string key;
        try
        {
            key = new ExpressionCacheKeyGenerator(expression).GetKey(keyParams);
        }
        catch (TargetInvocationException)
        {
            key = new StringCacheKeyGenerator().GetKey(keyParams);
        }

        key = $"Query:{key.ToMd5Hash()}";
        _logger.LogDebug($"get cache key: {key}");
        return key;
    }

    #endregion

    /// <summary>
    /// 获取或添加全局缓存,不考虑租户
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="factory">缓存数据获取工厂</param>
    /// <param name="expiration">过期时间</param>
    /// <returns>缓存数据</returns>
    public T GetOrAddGlobal<T>(string key, Func<T> factory, TimeSpan? expiration = null)
    {
        Check.NotNullOrEmpty(key, nameof(key));
        Check.NotNull(factory, nameof(factory));

        if (_globalKeyGenerator == null)
        {
            throw new OsharpException("当前缓存键生成器不支持全局缓存键生成");
        }

        string cacheKey = _globalKeyGenerator.GetGlobalKey(key);
        T result = Get<T>(cacheKey);
        if (!Equals(result, default(T)))
        {
            return result;
        }

        result = factory();
        if (Equals(result, default(T)))
        {
            return default;
        }

        Set(cacheKey, result, expiration);
        return result;
    }

    /// <summary>
    /// 异步获取或添加全局缓存,不考虑租户
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="factory">缓存数据获取工厂</param>
    /// <param name="expiration">过期时间</param>
    /// <returns>缓存数据</returns>
    public async Task<T> GetOrAddGlobalAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null)
    {
        Check.NotNullOrEmpty(key, nameof(key));
        Check.NotNull(factory, nameof(factory));

        if (_globalKeyGenerator == null)
        {
            throw new OsharpException("当前缓存键生成器不支持全局缓存键生成");
        }

        string cacheKey = await _globalKeyGenerator.GetGlobalKeyAsync(key);
        T result = await GetAsync<T>(cacheKey);
        if (!Equals(result, default(T)))
        {
            return result;
        }

        result = await factory();
        if (Equals(result, default(T)))
        {
            return default;
        }

        await SetAsync(cacheKey, result, expiration);
        return result;
    }

    /// <summary>
    /// 移除全局缓存,不考虑租户
    /// </summary>
    /// <param name="key">缓存键</param>
    public void RemoveGlobal(string key)
    {
        Check.NotNullOrEmpty(key, nameof(key));

        if (_globalKeyGenerator == null)
        {
            throw new OsharpException("当前缓存键生成器不支持全局缓存键生成");
        }

        string cacheKey = _globalKeyGenerator.GetGlobalKey(key);
        _cache.Remove(cacheKey);
    }

    /// <summary>
    /// 异步移除全局缓存,不考虑租户
    /// </summary>
    /// <param name="key">缓存键</param>
    public async Task RemoveGlobalAsync(string key)
    {
        Check.NotNullOrEmpty(key, nameof(key));

        if (_globalKeyGenerator == null)
        {
            throw new OsharpException("当前缓存键生成器不支持全局缓存键生成");
        }

        string cacheKey = await _globalKeyGenerator.GetGlobalKeyAsync(key);
        await _cache.RemoveAsync(cacheKey);
    }

    // 在 CacheService 类中添加以下方法

    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    /// <param name="expiration">过期时间</param>
    public void Set<T>(string key, T value, TimeSpan? expiration = null)
    {
        Check.NotNullOrEmpty(key, nameof(key));

        if (value == null)
        {
            return;
        }

        var options = new DistributedCacheEntryOptions();
        if (expiration.HasValue)
        {
            options.AbsoluteExpirationRelativeToNow = expiration;
        }

        string json = JsonSerializer.Serialize(value);
        _cache.SetString(key, json, options);
    }

    /// <summary>
    /// 异步设置缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    /// <param name="expiration">过期时间</param>
    /// <returns>异步任务</returns>
    public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null)
    {
        Check.NotNullOrEmpty(key, nameof(key));

        if (value == null)
        {
            return;
        }

        var options = new DistributedCacheEntryOptions();
        if (expiration.HasValue)
        {
            options.AbsoluteExpirationRelativeToNow = expiration;
        }

        string json = JsonSerializer.Serialize(value);
        await _cache.SetStringAsync(key, json, options);
    }

    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns>缓存数据</returns>
    public T Get<T>(string key)
    {
        Check.NotNullOrEmpty(key, nameof(key));

        string json = _cache.GetString(key);
        if (string.IsNullOrEmpty(json))
        {
            return default;
        }

        return JsonSerializer.Deserialize<T>(json);
    }

    /// <summary>
    /// 异步获取缓存
    /// </summary>
    /// <typeparam name="T">缓存数据类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns>缓存数据</returns>
    public async Task<T> GetAsync<T>(string key)
    {
        Check.NotNullOrEmpty(key, nameof(key));

        string json = await _cache.GetStringAsync(key);
        if (string.IsNullOrEmpty(json))
        {
            return default;
        }

        return JsonSerializer.Deserialize<T>(json);
    }
}