mirror of
https://github.com/anjoy8/Blog.Core.git
synced 2024-09-20 23:48:27 +08:00
✨🎨 增加多租户-分库方案
1.自动初始化维护租户库 2.多租户库种子数据维护 分库方案 TenantByDbController
This commit is contained in:
parent
1cc3ef25fa
commit
296201998d
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -355,3 +355,4 @@ Blog.Core/WMBlog.db
|
|||
Blog.Core/Blog.Core*.xml
|
||||
Blog.Core.Api/WMBlog.db
|
||||
Blog.Core.Api/wwwroot/ui/
|
||||
*.db
|
||||
|
|
|
@ -34,21 +34,6 @@
|
|||
所有
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.CustomEnums.TenantTypeEnum">
|
||||
<summary>
|
||||
租户隔离方案
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Blog.Core.Model.CustomEnums.TenantTypeEnum.Id">
|
||||
<summary>
|
||||
Id隔离
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Blog.Core.Model.CustomEnums.TenantTypeEnum.Db">
|
||||
<summary>
|
||||
库隔离
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.IDS4DbModels.ApplicationRole">
|
||||
<summary>
|
||||
以下model 来自ids4项目,多库模式,为了调取ids4数据
|
||||
|
@ -316,7 +301,16 @@
|
|||
逻辑删除
|
||||
</summary>
|
||||
</member>
|
||||
|
||||
<member name="P:Blog.Core.Model.Models.BlogArticle.Comments">
|
||||
<summary>
|
||||
评论
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.BlogArticleComment">
|
||||
<summary>
|
||||
博客文章 评论
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.BusinessTable">
|
||||
<summary>
|
||||
业务数据 <br/>
|
||||
|
@ -336,16 +330,6 @@
|
|||
<member name="P:Blog.Core.Model.Models.BusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
|
||||
<member name="P:Blog.Core.Model.Models.BlogArticle.Comments">
|
||||
<summary>
|
||||
评论
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.BlogArticleComment">
|
||||
<summary>
|
||||
博客文章 评论
|
||||
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.Department">
|
||||
|
@ -933,14 +917,20 @@
|
|||
软删除 过滤器
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.RootTkey.Interface.ITenantEntity">
|
||||
<member name="T:Blog.Core.Model.Models.SubLibraryBusinessTable">
|
||||
<summary>
|
||||
租户模型接口
|
||||
多租户-多库方案 业务表 <br/>
|
||||
公共库无需标记[MultiTenant]特性
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.RootTkey.Interface.ITenantEntity.TenantId">
|
||||
<member name="P:Blog.Core.Model.Models.SubLibraryBusinessTable.Name">
|
||||
<summary>
|
||||
租户Id
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.SubLibraryBusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.SysTenant">
|
||||
|
@ -1892,6 +1882,37 @@
|
|||
返回数据集
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Tenants.ITenantEntity">
|
||||
<summary>
|
||||
租户模型接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Tenants.ITenantEntity.TenantId">
|
||||
<summary>
|
||||
租户Id
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Tenants.MultiTenantAttribute">
|
||||
<summary>
|
||||
标识 多租户-分库 的业务表 <br/>
|
||||
公共表无需区分 直接使用主库 各自业务在各自库中
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Tenants.TenantTypeEnum">
|
||||
<summary>
|
||||
租户隔离方案
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Blog.Core.Model.Tenants.TenantTypeEnum.Id">
|
||||
<summary>
|
||||
Id隔离
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Blog.Core.Model.Tenants.TenantTypeEnum.Db">
|
||||
<summary>
|
||||
库隔离
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.ViewModels.AdvertisementViewModels">
|
||||
<summary>
|
||||
广告类
|
||||
|
|
|
@ -1236,11 +1236,22 @@
|
|||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantByIdController">
|
||||
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantByDbController">
|
||||
<summary>
|
||||
多租户测试
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Blog.Core.Api.Controllers.Tenant.TenantByDbController.GetAll">
|
||||
<summary>
|
||||
获取租户下全部业务数据 <br/>
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantByIdController">
|
||||
<summary>
|
||||
多租户-Id方案 测试
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Blog.Core.Api.Controllers.Tenant.TenantByIdController.GetAll">
|
||||
<summary>
|
||||
获取租户下全部业务数据 <br/>
|
||||
|
|
38
Blog.Core.Api/Controllers/Tenant/TenantByDbController.cs
Normal file
38
Blog.Core.Api/Controllers/Tenant/TenantByDbController.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using Blog.Core.Common.HttpContextUser;
|
||||
using Blog.Core.Controllers;
|
||||
using Blog.Core.IServices.BASE;
|
||||
using Blog.Core.Model;
|
||||
using Blog.Core.Model.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Blog.Core.Api.Controllers.Tenant;
|
||||
|
||||
/// <summary>
|
||||
/// 多租户测试
|
||||
/// </summary>
|
||||
[Produces("application/json")]
|
||||
[Route("api/Tenant/ByDb")]
|
||||
[Authorize]
|
||||
public class TenantByDbController : BaseApiController
|
||||
{
|
||||
private readonly IBaseServices<SubLibraryBusinessTable> _services;
|
||||
private readonly IUser _user;
|
||||
|
||||
public TenantByDbController(IUser user, IBaseServices<SubLibraryBusinessTable> services)
|
||||
{
|
||||
_user = user;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取租户下全部业务数据 <br/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<MessageModel<List<SubLibraryBusinessTable>>> GetAll()
|
||||
{
|
||||
var data = await _services.Query();
|
||||
return Success(data);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
namespace Blog.Core.Api.Controllers.Tenant;
|
||||
|
||||
/// <summary>
|
||||
/// 多租户测试
|
||||
/// 多租户-Id方案 测试
|
||||
/// </summary>
|
||||
[Produces("application/json")]
|
||||
[Route("api/Tenant/ById")]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
|
||||
namespace Blog.Core.Common.Core;
|
||||
|
@ -8,8 +8,10 @@ public static class InternalApp
|
|||
/// <summary>根服务</summary>
|
||||
public static IServiceProvider RootServices;
|
||||
|
||||
public static void ConfigureApplication(this IHost app)
|
||||
public static void ConfigureApplication(this WebApplication app)
|
||||
{
|
||||
RootServices = app.Services;
|
||||
app.Lifetime.ApplicationStarted.Register(() => { InternalApp.RootServices = app.Services; });
|
||||
|
||||
app.Lifetime.ApplicationStopped.Register(() => { InternalApp.RootServices = null; });
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
using Blog.Core.Model.Models.RootTkey;
|
||||
using Blog.Core.Model.Models.RootTkey.Interface;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Blog.Core.Common.DB
|
|||
* 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。
|
||||
*/
|
||||
public static (List<MutiDBOperate> allDbs, List<MutiDBOperate> slaveDbs) MutiConnectionString => MutiInitConn();
|
||||
|
||||
private static string DifDBConnOfSecurity(params string[] conn)
|
||||
{
|
||||
foreach (var item in conn)
|
||||
|
@ -22,7 +23,9 @@ namespace Blog.Core.Common.DB
|
|||
return File.ReadAllText(item).Trim();
|
||||
}
|
||||
}
|
||||
catch (System.Exception) { }
|
||||
catch (System.Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return conn[conn.Length - 1];
|
||||
|
@ -37,8 +40,9 @@ namespace Blog.Core.Common.DB
|
|||
{
|
||||
SpecialDbString(i);
|
||||
}
|
||||
List<MutiDBOperate> listdatabaseSimpleDB = new List<MutiDBOperate>();//单库
|
||||
List<MutiDBOperate> listdatabaseSlaveDB = new List<MutiDBOperate>();//从库
|
||||
|
||||
List<MutiDBOperate> listdatabaseSimpleDB = new List<MutiDBOperate>(); //单库
|
||||
List<MutiDBOperate> listdatabaseSlaveDB = new List<MutiDBOperate>(); //从库
|
||||
|
||||
// 单库,且不开启读写分离,只保留一个
|
||||
if (!AppSettings.app(new string[] { "CQRSEnabled" }).ObjToBool() && !AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool())
|
||||
|
@ -54,6 +58,7 @@ namespace Blog.Core.Common.DB
|
|||
{
|
||||
dbFirst = listdatabase.FirstOrDefault();
|
||||
}
|
||||
|
||||
listdatabaseSimpleDB.Add(dbFirst);
|
||||
return (listdatabaseSimpleDB, listdatabaseSlaveDB);
|
||||
}
|
||||
|
@ -70,7 +75,6 @@ namespace Blog.Core.Common.DB
|
|||
}
|
||||
|
||||
|
||||
|
||||
return (listdatabase, listdatabaseSlaveDB);
|
||||
//}
|
||||
}
|
||||
|
@ -115,24 +119,29 @@ namespace Blog.Core.Common.DB
|
|||
Dm = 5,
|
||||
Kdbndp = 6,
|
||||
}
|
||||
|
||||
public class MutiDBOperate
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接启用开关
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接ID
|
||||
/// </summary>
|
||||
public string ConnId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 从库执行级别,越大越先执行
|
||||
/// </summary>
|
||||
public int HitRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接字符串
|
||||
/// </summary>
|
||||
public string Connection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据库类型
|
||||
/// </summary>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Blog.Core.Model.Models.RootTkey;
|
||||
using Blog.Core.Model.Models.RootTkey.Interface;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
|
28
Blog.Core.Common/DB/TenantUtil.cs
Normal file
28
Blog.Core.Common/DB/TenantUtil.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Blog.Core.Model.Models;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Common.DB;
|
||||
|
||||
public static class TenantUtil
|
||||
{
|
||||
public static ConnectionConfig GetConnectionConfig(this SysTenant tenant)
|
||||
{
|
||||
if (tenant.DbType is null)
|
||||
{
|
||||
throw new ArgumentException("Tenant DbType Must");
|
||||
}
|
||||
|
||||
return new ConnectionConfig()
|
||||
{
|
||||
ConfigId = tenant.ConfigId,
|
||||
DbType = tenant.DbType.Value,
|
||||
ConnectionString = tenant.Connection,
|
||||
IsAutoCloseConnection = true,
|
||||
MoreSettings = new ConnMoreSettings()
|
||||
{
|
||||
IsAutoRemoveDataCache = true
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Common.Seed
|
||||
{
|
||||
|
@ -97,7 +99,9 @@ namespace Blog.Core.Common.Seed
|
|||
var modelTypes = referencedAssemblies
|
||||
.SelectMany(a => a.DefinedTypes)
|
||||
.Select(type => type.AsType())
|
||||
.Where(x => x.IsClass && x.Namespace is "Blog.Core.Model.Models").ToList();
|
||||
.Where(x => x.IsClass && x.Namespace is "Blog.Core.Model.Models")
|
||||
.Where(s => !s.IsDefined(typeof(MultiTenantAttribute), false))
|
||||
.ToList();
|
||||
modelTypes.ForEach(t =>
|
||||
{
|
||||
// 这里只支持添加表,不支持删除
|
||||
|
@ -326,7 +330,7 @@ namespace Blog.Core.Common.Seed
|
|||
#endregion
|
||||
|
||||
//种子初始化
|
||||
await SeedDataAsync(myContext);
|
||||
await SeedDataAsync(myContext.Db);
|
||||
|
||||
ConsoleHelper.WriteSuccessLine($"Done seeding database!");
|
||||
}
|
||||
|
@ -347,12 +351,28 @@ namespace Blog.Core.Common.Seed
|
|||
/// </summary>
|
||||
/// <param name="myContext"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task SeedDataAsync(MyContext myContext)
|
||||
private static async Task SeedDataAsync(ISqlSugarClient db)
|
||||
{
|
||||
// 获取所有种子配置-初始化数据
|
||||
var seedDataTypes = AssemblysExtensions.GetAllAssemblies().SelectMany(s => s.DefinedTypes)
|
||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass &&
|
||||
u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))));
|
||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass)
|
||||
.Where(u =>
|
||||
{
|
||||
var esd = u.GetInterfaces().FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>)));
|
||||
if (esd is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var eType = esd.GenericTypeArguments[0];
|
||||
if (eType.GetCustomAttribute<MultiTenantAttribute>() is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!seedDataTypes.Any()) return;
|
||||
foreach (var seedType in seedDataTypes)
|
||||
{
|
||||
|
@ -363,11 +383,11 @@ namespace Blog.Core.Common.Seed
|
|||
if (seedData != null && Enumerable.Any(seedData))
|
||||
{
|
||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||
var entity = myContext.Db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
var entity = db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
|
||||
if (!await myContext.Db.Queryable(entity.DbTableName, "").AnyAsync())
|
||||
if (!await db.Queryable(entity.DbTableName, "").AnyAsync())
|
||||
{
|
||||
await myContext.Db.Insertable(Enumerable.ToList(seedData)).ExecuteCommandAsync();
|
||||
await db.Insertable(Enumerable.ToList(seedData)).ExecuteCommandAsync();
|
||||
Console.WriteLine($"Table:{entity.DbTableName} init success!");
|
||||
}
|
||||
}
|
||||
|
@ -379,18 +399,132 @@ namespace Blog.Core.Common.Seed
|
|||
if (seedData != null && Enumerable.Any(seedData))
|
||||
{
|
||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||
var entity = myContext.Db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
var entity = db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
|
||||
await myContext.Db.Storageable(Enumerable.ToList(seedData)).ExecuteCommandAsync();
|
||||
await db.Storageable(Enumerable.ToList(seedData)).ExecuteCommandAsync();
|
||||
Console.WriteLine($"Table:{entity.DbTableName} seedData success!");
|
||||
}
|
||||
}
|
||||
|
||||
//自定义处理
|
||||
{
|
||||
await instance.CustomizeSeedData(myContext.Db);
|
||||
await instance.CustomizeSeedData(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 多租户
|
||||
/// </summary>
|
||||
/// <param name="myContext"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task TenantSeedAsync(MyContext myContext)
|
||||
{
|
||||
var tenants = await myContext.Db.Queryable<SysTenant>().Where(s => s.TenantType == TenantTypeEnum.Db).ToListAsync();
|
||||
if (!tenants.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($@"Init Multi Tenant Db");
|
||||
foreach (var tenant in tenants)
|
||||
{
|
||||
Console.WriteLine($@"Init Multi Tenant Db : {tenant.ConfigId}/{tenant.Name}");
|
||||
await InitTenantSeedAsync(myContext.Db.AsTenant(), tenant.GetConnectionConfig());
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task InitTenantSeedAsync(ITenant itenant, ConnectionConfig config)
|
||||
{
|
||||
itenant.AddConnection(config);
|
||||
|
||||
var db = itenant.GetConnectionScope(config.ConfigId);
|
||||
|
||||
db.DbMaintenance.CreateDatabase();
|
||||
ConsoleHelper.WriteSuccessLine($"Init Multi Tenant Db : {config.ConfigId} Database created successfully!");
|
||||
|
||||
|
||||
Console.WriteLine($@"Init Multi Tenant Db : {config.ConfigId} Create Tables");
|
||||
// 获取所有实体表-初始化租户业务表
|
||||
var entityTypes = RepositorySetting.Entitys
|
||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass)
|
||||
.Where(s => s.IsDefined(typeof(MultiTenantAttribute), false));
|
||||
if (!entityTypes.Any()) return;
|
||||
foreach (var entityType in entityTypes)
|
||||
{
|
||||
var splitTable = entityType.GetCustomAttribute<SplitTableAttribute>();
|
||||
if (splitTable == null)
|
||||
db.CodeFirst.InitTables(entityType);
|
||||
else
|
||||
db.CodeFirst.SplitTables().InitTables(entityType);
|
||||
|
||||
Console.WriteLine(entityType.Name);
|
||||
}
|
||||
|
||||
//多租户初始化种子数据
|
||||
await TenantSeedDataAsync(db);
|
||||
}
|
||||
|
||||
private static async Task TenantSeedDataAsync(ISqlSugarClient db)
|
||||
{
|
||||
// 获取所有种子配置-初始化数据
|
||||
var seedDataTypes = AssemblysExtensions.GetAllAssemblies().SelectMany(s => s.DefinedTypes)
|
||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass)
|
||||
.Where(u =>
|
||||
{
|
||||
var esd = u.GetInterfaces().FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>)));
|
||||
if (esd is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var eType = esd.GenericTypeArguments[0];
|
||||
if (eType.GetCustomAttribute<MultiTenantAttribute>() is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (!seedDataTypes.Any()) return;
|
||||
foreach (var seedType in seedDataTypes)
|
||||
{
|
||||
dynamic instance = Activator.CreateInstance(seedType);
|
||||
//初始化数据
|
||||
{
|
||||
var seedData = instance.InitSeedData();
|
||||
if (seedData != null && Enumerable.Any(seedData))
|
||||
{
|
||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||
var entity = db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
|
||||
if (!await db.Queryable(entity.DbTableName, "").AnyAsync())
|
||||
{
|
||||
await db.Insertable(Enumerable.ToList(seedData)).ExecuteCommandAsync();
|
||||
Console.WriteLine($"Table:{entity.DbTableName} init success!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//种子数据
|
||||
{
|
||||
var seedData = instance.SeedData();
|
||||
if (seedData != null && Enumerable.Any(seedData))
|
||||
{
|
||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||
var entity = db.EntityMaintenance.GetEntityInfo(entityType);
|
||||
|
||||
await db.Storageable(Enumerable.ToList(seedData)).ExecuteCommandAsync();
|
||||
Console.WriteLine($"Table:{entity.DbTableName} seedData success!");
|
||||
}
|
||||
}
|
||||
|
||||
//自定义处理
|
||||
{
|
||||
await instance.CustomizeSeedData(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
Blog.Core.Common/Seed/SeedData/SubBusinessDataSeedData.cs
Normal file
70
Blog.Core.Common/Seed/SeedData/SubBusinessDataSeedData.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using Blog.Core.Model.Models;
|
||||
using SqlSugar;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Blog.Core.Common.Seed.SeedData;
|
||||
|
||||
public class SubBusinessDataSeedData : IEntitySeedData<SubLibraryBusinessTable>
|
||||
{
|
||||
public IEnumerable<SubLibraryBusinessTable> InitSeedData()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public IEnumerable<SubLibraryBusinessTable> SeedData()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task CustomizeSeedData(ISqlSugarClient db)
|
||||
{
|
||||
//初始化分库数据
|
||||
//只是用于测试
|
||||
if (db.CurrentConnectionConfig.ConfigId == "Tenant_3")
|
||||
{
|
||||
if (!await db.Queryable<SubLibraryBusinessTable>().AnyAsync())
|
||||
{
|
||||
await db.Insertable<SubLibraryBusinessTable>(new List<SubLibraryBusinessTable>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = SnowFlakeSingle.Instance.NextId(),
|
||||
Name = "王五业务数据1",
|
||||
Amount = 100,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = SnowFlakeSingle.Instance.NextId(),
|
||||
Name = "王五业务数据2",
|
||||
Amount = 1000,
|
||||
},
|
||||
}).ExecuteReturnSnowflakeIdListAsync();
|
||||
}
|
||||
}
|
||||
else if (db.CurrentConnectionConfig.ConfigId == "Tenant_4")
|
||||
{
|
||||
if (!await db.Queryable<SubLibraryBusinessTable>().AnyAsync())
|
||||
{
|
||||
await db.Insertable<SubLibraryBusinessTable>(new List<SubLibraryBusinessTable>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = SnowFlakeSingle.Instance.NextId(),
|
||||
Name = "赵六业务数据1",
|
||||
Amount = 50,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = SnowFlakeSingle.Instance.NextId(),
|
||||
Name = "赵六业务数据2",
|
||||
Amount = 60,
|
||||
},
|
||||
}).ExecuteReturnSnowflakeIdListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await Task.Delay(1);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Blog.Core.Model.CustomEnums;
|
||||
using Blog.Core.Common.DB;
|
||||
using Blog.Core.Model.Models;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Common.Seed.SeedData;
|
||||
|
@ -29,6 +32,24 @@ public class TenantSeedData : IEntitySeedData<SysTenant>
|
|||
Name = "李四",
|
||||
TenantType = TenantTypeEnum.Id
|
||||
},
|
||||
new SysTenant()
|
||||
{
|
||||
Id = 1000003,
|
||||
ConfigId = "Tenant_3",
|
||||
Name = "王五",
|
||||
TenantType = TenantTypeEnum.Db,
|
||||
DbType = DbType.Sqlite,
|
||||
Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, "WangWu.db"),
|
||||
},
|
||||
new SysTenant()
|
||||
{
|
||||
Id = 1000004,
|
||||
ConfigId = "Tenant_4",
|
||||
Name = "赵六",
|
||||
TenantType = TenantTypeEnum.Db,
|
||||
DbType = DbType.Sqlite,
|
||||
Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, "ZhaoLiu.db"),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Blog.Core.Model.Models;
|
||||
using SqlSugar;
|
||||
|
@ -14,7 +15,12 @@ public class UserInfoSeedData : IEntitySeedData<SysUserInfo>
|
|||
|
||||
public IEnumerable<SysUserInfo> SeedData()
|
||||
{
|
||||
return new[]
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task CustomizeSeedData(ISqlSugarClient db)
|
||||
{
|
||||
var data = new List<SysUserInfo>()
|
||||
{
|
||||
new SysUserInfo()
|
||||
{
|
||||
|
@ -32,11 +38,35 @@ public class UserInfoSeedData : IEntitySeedData<SysUserInfo>
|
|||
Name = "李四",
|
||||
TenantId = 1000002, //租户Id
|
||||
},
|
||||
new SysUserInfo()
|
||||
{
|
||||
Id = 10003,
|
||||
LoginName = "wangwu",
|
||||
LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E",
|
||||
Name = "王五",
|
||||
TenantId = 1000003, //租户Id
|
||||
},
|
||||
new SysUserInfo()
|
||||
{
|
||||
Id = 10004,
|
||||
LoginName = "zhaoliu",
|
||||
LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E",
|
||||
Name = "赵六",
|
||||
TenantId = 1000004, //租户Id
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public Task CustomizeSeedData(ISqlSugarClient db)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
var names = data.Select(s => s.LoginName).ToList();
|
||||
names = await db.Queryable<SysUserInfo>()
|
||||
.Where(s => names.Contains(s.LoginName))
|
||||
.Select(s => s.LoginName).ToListAsync();
|
||||
|
||||
var sysUserInfos = data.Where(s => !names.Contains(s.LoginName)).ToList();
|
||||
if (sysUserInfos.Any())
|
||||
{
|
||||
await db.Insertable<SysUserInfo>(sysUserInfos).ExecuteReturnIdentityAsync();
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -38,6 +38,9 @@ public sealed class SeedDataHostedService : IHostedService
|
|||
if (AppSettings.app("AppSettings", "SeedDBEnabled").ObjToBool() || AppSettings.app("AppSettings", "SeedDBDataEnabled").ObjToBool())
|
||||
{
|
||||
await DBSeed.SeedAsync(_myContext, _webRootPath);
|
||||
|
||||
//多租户 同步
|
||||
await DBSeed.TenantSeedAsync(_myContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Blog.Core.Model.Models.RootTkey;
|
||||
using Blog.Core.Model.Models.RootTkey.Interface;
|
||||
using Blog.Core.Model.Tenants;
|
||||
|
||||
namespace Blog.Core.Model.Models;
|
||||
|
||||
|
|
22
Blog.Core.Model/Models/SubLibraryBusinessTable.cs
Normal file
22
Blog.Core.Model/Models/SubLibraryBusinessTable.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using Blog.Core.Model.Models.RootTkey;
|
||||
using Blog.Core.Model.Tenants;
|
||||
|
||||
namespace Blog.Core.Model.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 多租户-多库方案 业务表 <br/>
|
||||
/// 公共库无需标记[MultiTenant]特性
|
||||
/// </summary>
|
||||
[MultiTenant]
|
||||
public class SubLibraryBusinessTable : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 金额
|
||||
/// </summary>
|
||||
public decimal Amount { get; set; }
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
using Blog.Core.Model.CustomEnums;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Model.Models;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Model.Models.RootTkey.Interface;
|
||||
namespace Blog.Core.Model.Tenants;
|
||||
|
||||
/// <summary>
|
||||
/// 租户模型接口
|
12
Blog.Core.Model/Tenants/MultiTenantAttribute.cs
Normal file
12
Blog.Core.Model/Tenants/MultiTenantAttribute.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace Blog.Core.Model.Tenants;
|
||||
|
||||
/// <summary>
|
||||
/// 标识 多租户-分库 的业务表 <br/>
|
||||
/// 公共表无需区分 直接使用主库 各自业务在各自库中
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class MultiTenantAttribute : Attribute
|
||||
{
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace Blog.Core.Model.CustomEnums;
|
||||
namespace Blog.Core.Model.Tenants;
|
||||
|
||||
/// <summary>
|
||||
/// 租户隔离方案
|
|
@ -8,6 +8,10 @@ using System.Data;
|
|||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Blog.Core.Common.DB;
|
||||
using Blog.Core.Common.HttpContextUser;
|
||||
using Blog.Core.Model.Models;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using Blog.Core.Repository.UnitOfWorks;
|
||||
|
||||
namespace Blog.Core.Repository.Base
|
||||
|
@ -22,11 +26,12 @@ namespace Blog.Core.Repository.Base
|
|||
get
|
||||
{
|
||||
ISqlSugarClient db = _dbBase;
|
||||
|
||||
/* 如果要开启多库支持,
|
||||
* 1、在appsettings.json 中开启MutiDBEnabled节点为true,必填
|
||||
* 2、设置一个主连接的数据库ID,节点MainDB,对应的连接字符串的Enabled也必须true,必填
|
||||
*/
|
||||
if (AppSettings.app(new[] {"MutiDBEnabled"}).ObjToBool())
|
||||
if (AppSettings.app(new[] { "MutiDBEnabled" }).ObjToBool())
|
||||
{
|
||||
//修改使用 model备注字段作为切换数据库条件,使用sqlsugar TenantAttribute存放数据库ConnId
|
||||
//参考 https://www.donet5.com/Home/Doc?typeId=2246
|
||||
|
@ -35,6 +40,27 @@ namespace Blog.Core.Repository.Base
|
|||
{
|
||||
//统一处理 configId 小写
|
||||
db = _dbBase.GetConnectionScope(tenantAttr.configId.ToString().ToLower());
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
//多租户
|
||||
if (typeof(TEntity).GetCustomAttribute<MultiTenantAttribute>() != null)
|
||||
{
|
||||
//获取租户信息 租户信息可以提前缓存下来
|
||||
if (App.User is { TenantId: > 0 })
|
||||
{
|
||||
var tenant = db.Queryable<SysTenant>().WithCache().Where(s => s.Id == App.User.TenantId).First();
|
||||
if (tenant != null)
|
||||
{
|
||||
var iTenant = db.AsTenant();
|
||||
if (!iTenant.IsAnyConnection(tenant.ConfigId))
|
||||
{
|
||||
iTenant.AddConnection(tenant.GetConnectionConfig());
|
||||
}
|
||||
|
||||
return iTenant.GetConnectionScope(tenant.ConfigId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,12 +77,12 @@ namespace Blog.Core.Repository.Base
|
|||
}
|
||||
|
||||
|
||||
|
||||
public async Task<TEntity> QueryById(object objId)
|
||||
{
|
||||
//return await Task.Run(() => _db.Queryable<TEntity>().InSingle(objId));
|
||||
return await _db.Queryable<TEntity>().In(objId).SingleAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 功能描述:根据ID查询一条数据
|
||||
/// 作 者:Blog.Core
|
||||
|
@ -161,25 +187,28 @@ namespace Blog.Core.Repository.Base
|
|||
}
|
||||
|
||||
public async Task<bool> Update(
|
||||
TEntity entity,
|
||||
List<string> lstColumns = null,
|
||||
List<string> lstIgnoreColumns = null,
|
||||
string where = ""
|
||||
)
|
||||
TEntity entity,
|
||||
List<string> lstColumns = null,
|
||||
List<string> lstIgnoreColumns = null,
|
||||
string where = ""
|
||||
)
|
||||
{
|
||||
IUpdateable<TEntity> up = _db.Updateable(entity);
|
||||
if (lstIgnoreColumns != null && lstIgnoreColumns.Count > 0)
|
||||
{
|
||||
up = up.IgnoreColumns(lstIgnoreColumns.ToArray());
|
||||
}
|
||||
|
||||
if (lstColumns != null && lstColumns.Count > 0)
|
||||
{
|
||||
up = up.UpdateColumns(lstColumns.ToArray());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(where))
|
||||
{
|
||||
up = up.Where(where);
|
||||
}
|
||||
|
||||
return await up.ExecuteCommandHasChangeAsync();
|
||||
}
|
||||
|
||||
|
@ -214,7 +243,6 @@ namespace Blog.Core.Repository.Base
|
|||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 功能描述:查询所有数据
|
||||
/// 作 者:Blog.Core
|
||||
|
@ -284,6 +312,7 @@ namespace Blog.Core.Repository.Base
|
|||
{
|
||||
return await _db.Queryable<TEntity>().WhereIF(whereExpression != null, whereExpression).OrderByIF(orderByFields != null, orderByFields).ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 功能描述:查询一个列表
|
||||
/// </summary>
|
||||
|
@ -393,18 +422,16 @@ namespace Blog.Core.Repository.Base
|
|||
/// <param name="orderByFields">排序字段,如name asc,age desc</param>
|
||||
/// <returns>数据列表</returns>
|
||||
public async Task<List<TEntity>> Query(
|
||||
string where,
|
||||
int pageIndex,
|
||||
int pageSize,
|
||||
|
||||
string orderByFields)
|
||||
string where,
|
||||
int pageIndex,
|
||||
int pageSize,
|
||||
string orderByFields)
|
||||
{
|
||||
return await _db.Queryable<TEntity>().OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(!string.IsNullOrEmpty(where), where).ToPageListAsync(pageIndex, pageSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询[使用版本,其他分页未测试]
|
||||
/// </summary>
|
||||
|
@ -415,12 +442,11 @@ namespace Blog.Core.Repository.Base
|
|||
/// <returns></returns>
|
||||
public async Task<PageModel<TEntity>> QueryPage(Expression<Func<TEntity, bool>> whereExpression, int pageIndex = 1, int pageSize = 20, string orderByFields = null)
|
||||
{
|
||||
|
||||
RefAsync<int> totalCount = 0;
|
||||
var list = await _db.Queryable<TEntity>()
|
||||
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(whereExpression != null, whereExpression)
|
||||
.ToPageListAsync(pageIndex, pageSize, totalCount);
|
||||
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(whereExpression != null, whereExpression)
|
||||
.ToPageListAsync(pageIndex, pageSize, totalCount);
|
||||
|
||||
return new PageModel<TEntity>(pageIndex, totalCount, pageSize, list);
|
||||
}
|
||||
|
@ -446,6 +472,7 @@ namespace Blog.Core.Repository.Base
|
|||
{
|
||||
return await _db.Queryable(joinExpression).Select(selectExpression).ToListAsync();
|
||||
}
|
||||
|
||||
return await _db.Queryable(joinExpression).Where(whereLambda).Select(selectExpression).ToListAsync();
|
||||
}
|
||||
|
||||
|
@ -471,13 +498,12 @@ namespace Blog.Core.Repository.Base
|
|||
int pageSize = 20,
|
||||
string orderByFields = null)
|
||||
{
|
||||
|
||||
RefAsync<int> totalCount = 0;
|
||||
var list = await _db.Queryable<T, T2>(joinExpression)
|
||||
.Select(selectExpression)
|
||||
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(whereExpression != null, whereExpression)
|
||||
.ToPageListAsync(pageIndex, pageSize, totalCount);
|
||||
.Select(selectExpression)
|
||||
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(whereExpression != null, whereExpression)
|
||||
.ToPageListAsync(pageIndex, pageSize, totalCount);
|
||||
return new PageModel<TResult>(pageIndex, totalCount, pageSize, list);
|
||||
}
|
||||
|
||||
|
@ -506,10 +532,10 @@ namespace Blog.Core.Repository.Base
|
|||
{
|
||||
RefAsync<int> totalCount = 0;
|
||||
var list = await _db.Queryable<T, T2>(joinExpression).GroupBy(groupExpression)
|
||||
.Select(selectExpression)
|
||||
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(whereExpression != null, whereExpression)
|
||||
.ToPageListAsync(pageIndex, pageSize, totalCount);
|
||||
.Select(selectExpression)
|
||||
.OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields)
|
||||
.WhereIF(whereExpression != null, whereExpression)
|
||||
.ToPageListAsync(pageIndex, pageSize, totalCount);
|
||||
return new PageModel<TResult>(pageIndex, totalCount, pageSize, list);
|
||||
}
|
||||
|
||||
|
@ -531,7 +557,5 @@ namespace Blog.Core.Repository.Base
|
|||
// groupName = s.groupName,
|
||||
// jobName = s.jobName
|
||||
// }, exp, s => new { s.uID, s.uRealName, s.groupName, s.jobName }, model.currentPage, model.pageSize, model.orderField + " " + model.orderType);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user