mirror of
https://github.com/anjoy8/Blog.Core.git
synced 2024-09-20 23:48:27 +08:00
⚡️🎨 完美优雅的处理多租户-分表方案
1.扩展原有的MultiTenantAttribute 标识多库、多表 2.扩展原有的种子数据生成 用于多表的种子数据 3.巧妙优雅使用Sqlsugar表映射 解决多租户分表问题,原有代码无需改动 登录用户如果是租户用户自动切换到租户分表 目前来看(如果想要升级业务 扩展SAAS) 多表方案:代码侵入最小 id方案:侵入最大,需要增加列 多库方案:相对少 如果是从0到1 最推荐多库 如果是从0.5到1 最推荐多表
This commit is contained in:
parent
9b2506101a
commit
bd484137a6
|
@ -98,7 +98,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\Tenant\" />
|
||||
<Folder Include="wwwroot\BlogCore.Data.excel\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -311,27 +311,6 @@
|
|||
博客文章 评论
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.BusinessTable">
|
||||
<summary>
|
||||
业务数据 <br/>
|
||||
多租户 (Id 隔离)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.BusinessTable.TenantId">
|
||||
<summary>
|
||||
无需手动赋值
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.BusinessTable.Name">
|
||||
<summary>
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.BusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.Department">
|
||||
<summary>
|
||||
部门表
|
||||
|
@ -917,22 +896,6 @@
|
|||
软删除 过滤器
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.SubLibraryBusinessTable">
|
||||
<summary>
|
||||
多租户-多库方案 业务表 <br/>
|
||||
公共库无需标记[MultiTenant]特性
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.SubLibraryBusinessTable.Name">
|
||||
<summary>
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.SubLibraryBusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.SysTenant">
|
||||
<summary>
|
||||
系统租户表 <br/>
|
||||
|
@ -1145,6 +1108,63 @@
|
|||
任务内存中的状态
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.BusinessTable">
|
||||
<summary>
|
||||
业务数据 <br/>
|
||||
多租户 (Id 隔离)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.BusinessTable.TenantId">
|
||||
<summary>
|
||||
无需手动赋值
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.BusinessTable.Name">
|
||||
<summary>
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.BusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.MultiBusinessSubTable">
|
||||
<summary>
|
||||
多租户-多表方案 业务表 子表 <br/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.MultiBusinessTable">
|
||||
<summary>
|
||||
多租户-多表方案 业务表 <br/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.MultiBusinessTable.Name">
|
||||
<summary>
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.MultiBusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.SubLibraryBusinessTable">
|
||||
<summary>
|
||||
多租户-多库方案 业务表 <br/>
|
||||
公共库无需标记[MultiTenant]特性
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.SubLibraryBusinessTable.Name">
|
||||
<summary>
|
||||
名称
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Blog.Core.Model.Models.SubLibraryBusinessTable.Amount">
|
||||
<summary>
|
||||
金额
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Models.Topic">
|
||||
<summary>
|
||||
Tibug 类别
|
||||
|
@ -1894,8 +1914,9 @@
|
|||
</member>
|
||||
<member name="T:Blog.Core.Model.Tenants.MultiTenantAttribute">
|
||||
<summary>
|
||||
标识 多租户-分库 的业务表 <br/>
|
||||
公共表无需区分 直接使用主库 各自业务在各自库中
|
||||
标识 多租户 的业务表 <br/>
|
||||
默认设置是多库 <br/>
|
||||
公共表无需区分 直接使用主库 各自业务在各自库中 <br/>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.Tenants.TenantTypeEnum">
|
||||
|
@ -1913,6 +1934,11 @@
|
|||
库隔离
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Blog.Core.Model.Tenants.TenantTypeEnum.Tables">
|
||||
<summary>
|
||||
表隔离
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Model.ViewModels.AdvertisementViewModels">
|
||||
<summary>
|
||||
广告类
|
||||
|
|
|
@ -1238,7 +1238,7 @@
|
|||
</member>
|
||||
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantByDbController">
|
||||
<summary>
|
||||
多租户测试
|
||||
多租户-多库方案 测试
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Blog.Core.Api.Controllers.Tenant.TenantByDbController.GetAll">
|
||||
|
@ -1270,6 +1270,23 @@
|
|||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantByTableController">
|
||||
<summary>
|
||||
多租户-多表方案 测试
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Blog.Core.Api.Controllers.Tenant.TenantByTableController.GetAll">
|
||||
<summary>
|
||||
获取租户下全部业务数据 <br/>
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Blog.Core.Api.Controllers.Tenant.TenantByTableController.Post(Blog.Core.Model.Models.MultiBusinessTable)">
|
||||
<summary>
|
||||
新增数据
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Blog.Core.Api.Controllers.Tenant.TenantManagerController">
|
||||
<summary>
|
||||
租户管理
|
||||
|
|
57
Blog.Core.Api/Controllers/Tenant/TenantByTableController.cs
Normal file
57
Blog.Core.Api/Controllers/Tenant/TenantByTableController.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
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/ByTable")]
|
||||
[Authorize]
|
||||
public class TenantByTableController : BaseApiController
|
||||
{
|
||||
private readonly IBaseServices<MultiBusinessTable> _services;
|
||||
private readonly IUser _user;
|
||||
|
||||
public TenantByTableController(IUser user, IBaseServices<MultiBusinessTable> services)
|
||||
{
|
||||
_user = user;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取租户下全部业务数据 <br/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<MessageModel<List<MultiBusinessTable>>> GetAll()
|
||||
{
|
||||
//查询
|
||||
// var data = await _services.Query();
|
||||
|
||||
//关联查询
|
||||
var data = await _services.Db
|
||||
.Queryable<MultiBusinessTable>()
|
||||
.Includes(s => s.Child)
|
||||
.ToListAsync();
|
||||
return Success(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<MessageModel> Post(MultiBusinessTable data)
|
||||
{
|
||||
await _services.Db.Insertable(data).ExecuteReturnSnowflakeIdAsync();
|
||||
|
||||
return Success();
|
||||
}
|
||||
}
|
|
@ -39,6 +39,10 @@ public class RepositorySetting
|
|||
return;
|
||||
}
|
||||
|
||||
//多租户 单表
|
||||
db.QueryFilter.AddTableFilter<ITenantEntity>(it => it.TenantId == App.User.TenantId || it.TenantId == 0);
|
||||
|
||||
//多租户 多表
|
||||
db.SetTenantTable(App.User.TenantId.ToString());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Blog.Core.Model.Models;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Common.DB;
|
||||
|
@ -19,7 +23,7 @@ public static class TenantUtil
|
|||
switch (tenant.DbType.Value)
|
||||
{
|
||||
case DbType.Sqlite:
|
||||
tenant.Connection = $"DataSource={Path.Combine(Environment.CurrentDirectory, tenant.ConfigId)}.db" ;
|
||||
tenant.Connection = $"DataSource={Path.Combine(Environment.CurrentDirectory, tenant.ConfigId)}.db";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -47,4 +51,52 @@ public static class TenantUtil
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
public static List<Type> GetTenantEntityTypes(TenantTypeEnum? tenantType = null)
|
||||
{
|
||||
return RepositorySetting.Entitys
|
||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass)
|
||||
.Where(s => IsTenantEntity(s, tenantType))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static bool IsTenantEntity(this Type u, TenantTypeEnum? tenantType = null)
|
||||
{
|
||||
var mta = u.GetCustomAttribute<MultiTenantAttribute>();
|
||||
if (mta is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tenantType != null)
|
||||
{
|
||||
if (mta.TenantType != tenantType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetTenantTableName(this Type type, ISqlSugarClient db, string id)
|
||||
{
|
||||
var entityInfo = db.EntityMaintenance.GetEntityInfo(type);
|
||||
return $@"{entityInfo.DbTableName}_{id}";
|
||||
}
|
||||
|
||||
public static string GetTenantTableName(this Type type, ISqlSugarClient db, SysTenant tenant)
|
||||
{
|
||||
return GetTenantTableName(type, db, tenant.Id.ToString());
|
||||
}
|
||||
|
||||
public static void SetTenantTable(this ISqlSugarClient db, string id)
|
||||
{
|
||||
var types = GetTenantEntityTypes(TenantTypeEnum.Tables);
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
db.MappingTables.Add(type.Name, type.GetTenantTableName(db, id));
|
||||
}
|
||||
}
|
||||
}
|
18
Blog.Core.Common/Extensions/UntilExtensions.cs
Normal file
18
Blog.Core.Common/Extensions/UntilExtensions.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Blog.Core.Common.Extensions;
|
||||
|
||||
public static class UntilExtensions
|
||||
{
|
||||
public static void AddOrModify<TKey, TValue>(this IDictionary<TKey, TValue> dic, TKey key, TValue value)
|
||||
{
|
||||
if (dic.TryGetValue(key, out _))
|
||||
{
|
||||
dic[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
dic.Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
using Blog.Core.Common.Extensions;
|
||||
using Blog.Core.Common.Helper;
|
||||
using Blog.Core.Model.Models;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using Magicodes.ExporterAndImporter.Excel;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
@ -11,8 +13,6 @@ 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
|
||||
{
|
||||
|
@ -422,19 +422,62 @@ namespace Blog.Core.Common.Seed
|
|||
public static async Task TenantSeedAsync(MyContext myContext)
|
||||
{
|
||||
var tenants = await myContext.Db.Queryable<SysTenant>().Where(s => s.TenantType == TenantTypeEnum.Db).ToListAsync();
|
||||
if (!tenants.Any())
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($@"Init Multi Tenant Db");
|
||||
foreach (var tenant in tenants)
|
||||
tenants = await myContext.Db.Queryable<SysTenant>().Where(s => s.TenantType == TenantTypeEnum.Tables).ToListAsync();
|
||||
if (tenants.Any())
|
||||
{
|
||||
Console.WriteLine($@"Init Multi Tenant Db : {tenant.ConfigId}/{tenant.Name}");
|
||||
await InitTenantSeedAsync(myContext.Db.AsTenant(), tenant.GetConnectionConfig());
|
||||
await InitTenantSeedAsync(myContext, tenants);
|
||||
}
|
||||
}
|
||||
|
||||
#region 多租户 多表 初始化
|
||||
|
||||
private static async Task InitTenantSeedAsync(MyContext myContext, List<SysTenant> tenants)
|
||||
{
|
||||
ConsoleHelper.WriteInfoLine($"Init Multi Tenant Tables : {myContext.Db.CurrentConnectionConfig.ConfigId}");
|
||||
|
||||
// 获取所有实体表-初始化租户业务表
|
||||
var entityTypes = TenantUtil.GetTenantEntityTypes(TenantTypeEnum.Tables);
|
||||
if (!entityTypes.Any()) return;
|
||||
|
||||
foreach (var sysTenant in tenants)
|
||||
{
|
||||
foreach (var entityType in entityTypes)
|
||||
{
|
||||
myContext.Db.CodeFirst
|
||||
.As(entityType, entityType.GetTenantTableName(myContext.Db, sysTenant))
|
||||
.InitTables(entityType);
|
||||
|
||||
Console.WriteLine($@"Init Tables:{entityType.GetTenantTableName(myContext.Db, sysTenant)}");
|
||||
}
|
||||
|
||||
myContext.Db.SetTenantTable(sysTenant.Id.ToString());
|
||||
//多租户初始化种子数据
|
||||
await TenantSeedDataAsync(myContext.Db, TenantTypeEnum.Tables);
|
||||
}
|
||||
|
||||
ConsoleHelper.WriteSuccessLine($"Init Multi Tenant Tables : {myContext.Db.CurrentConnectionConfig.ConfigId} created successfully!");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 多租户 多库 初始化
|
||||
|
||||
/// <summary>
|
||||
/// 初始化多库
|
||||
/// </summary>
|
||||
/// <param name="itenant"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task InitTenantSeedAsync(ITenant itenant, ConnectionConfig config)
|
||||
{
|
||||
itenant.RemoveConnection(config.ConfigId);
|
||||
|
@ -445,12 +488,10 @@ namespace Blog.Core.Common.Seed
|
|||
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));
|
||||
var entityTypes = TenantUtil.GetTenantEntityTypes(TenantTypeEnum.Db);
|
||||
if (!entityTypes.Any()) return;
|
||||
foreach (var entityType in entityTypes)
|
||||
{
|
||||
|
@ -464,10 +505,12 @@ namespace Blog.Core.Common.Seed
|
|||
}
|
||||
|
||||
//多租户初始化种子数据
|
||||
await TenantSeedDataAsync(db);
|
||||
await TenantSeedDataAsync(db, TenantTypeEnum.Db);
|
||||
}
|
||||
|
||||
private static async Task TenantSeedDataAsync(ISqlSugarClient db)
|
||||
#endregion
|
||||
|
||||
private static async Task TenantSeedDataAsync(ISqlSugarClient db, TenantTypeEnum tenantType)
|
||||
{
|
||||
// 获取所有种子配置-初始化数据
|
||||
var seedDataTypes = AssemblysExtensions.GetAllAssemblies().SelectMany(s => s.DefinedTypes)
|
||||
|
@ -481,12 +524,7 @@ namespace Blog.Core.Common.Seed
|
|||
}
|
||||
|
||||
var eType = esd.GenericTypeArguments[0];
|
||||
if (eType.GetCustomAttribute<MultiTenantAttribute>() is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return eType.IsTenantEntity(tenantType);
|
||||
});
|
||||
if (!seedDataTypes.Any()) return;
|
||||
foreach (var seedType in seedDataTypes)
|
||||
|
|
38
Blog.Core.Common/Seed/SeedData/MultiBusinessDataSeedData.cs
Normal file
38
Blog.Core.Common/Seed/SeedData/MultiBusinessDataSeedData.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using Blog.Core.Model.Models;
|
||||
using SqlSugar;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Blog.Core.Common.Seed.SeedData;
|
||||
|
||||
public class MultiBusinessDataSeedData : IEntitySeedData<MultiBusinessTable>
|
||||
{
|
||||
public IEnumerable<MultiBusinessTable> InitSeedData()
|
||||
{
|
||||
return new List<MultiBusinessTable>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 1001,
|
||||
Name = "业务数据1",
|
||||
Amount = 100,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 1002,
|
||||
Name = "业务数据2",
|
||||
Amount = 1000,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<MultiBusinessTable> SeedData()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task CustomizeSeedData(ISqlSugarClient db)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using Blog.Core.Model.Models;
|
||||
using SqlSugar;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Blog.Core.Common.Seed.SeedData;
|
||||
|
||||
public class MultiBusinessSubDataSeedData : IEntitySeedData<MultiBusinessSubTable>
|
||||
{
|
||||
public IEnumerable<MultiBusinessSubTable> InitSeedData()
|
||||
{
|
||||
return new List<MultiBusinessSubTable>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Id = 100,
|
||||
MainId = 1001,
|
||||
Memo = "子数据",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = 1001,
|
||||
MainId = 1001,
|
||||
Memo = "子数据2",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<MultiBusinessSubTable> SeedData()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task CustomizeSeedData(ISqlSugarClient db)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -50,6 +50,13 @@ public class TenantSeedData : IEntitySeedData<SysTenant>
|
|||
DbType = DbType.Sqlite,
|
||||
Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, "ZhaoLiu.db"),
|
||||
},
|
||||
new SysTenant()
|
||||
{
|
||||
Id = 1000005,
|
||||
ConfigId = "Tenant_5",
|
||||
Name = "孙七",
|
||||
TenantType = TenantTypeEnum.Tables,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,14 @@ public class UserInfoSeedData : IEntitySeedData<SysUserInfo>
|
|||
Name = "赵六",
|
||||
TenantId = 1000004, //租户Id
|
||||
},
|
||||
new SysUserInfo()
|
||||
{
|
||||
Id = 10005,
|
||||
LoginName = "sunqi",
|
||||
LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E",
|
||||
Name = "孙七",
|
||||
TenantId = 1000005, //租户Id
|
||||
},
|
||||
};
|
||||
|
||||
var names = data.Select(s => s.LoginName).ToList();
|
||||
|
|
14
Blog.Core.Model/Models/Tenant/MultiBusinessSubTable.cs
Normal file
14
Blog.Core.Model/Models/Tenant/MultiBusinessSubTable.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Blog.Core.Model.Models.RootTkey;
|
||||
using Blog.Core.Model.Tenants;
|
||||
|
||||
namespace Blog.Core.Model.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 多租户-多表方案 业务表 子表 <br/>
|
||||
/// </summary>
|
||||
[MultiTenant(TenantTypeEnum.Tables)]
|
||||
public class MultiBusinessSubTable : BaseEntity
|
||||
{
|
||||
public long MainId { get; set; }
|
||||
public string Memo { get; set; }
|
||||
}
|
26
Blog.Core.Model/Models/Tenant/MultiBusinessTable.cs
Normal file
26
Blog.Core.Model/Models/Tenant/MultiBusinessTable.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System.Collections.Generic;
|
||||
using Blog.Core.Model.Models.RootTkey;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Blog.Core.Model.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 多租户-多表方案 业务表 <br/>
|
||||
/// </summary>
|
||||
[MultiTenant(TenantTypeEnum.Tables)]
|
||||
public class MultiBusinessTable : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 金额
|
||||
/// </summary>
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
[Navigate(NavigateType.OneToMany, nameof(MultiBusinessSubTable.MainId))]
|
||||
public List<MultiBusinessSubTable> Child { get; set; }
|
||||
}
|
|
@ -3,10 +3,22 @@
|
|||
namespace Blog.Core.Model.Tenants;
|
||||
|
||||
/// <summary>
|
||||
/// 标识 多租户-分库 的业务表 <br/>
|
||||
/// 公共表无需区分 直接使用主库 各自业务在各自库中
|
||||
/// 标识 多租户 的业务表 <br/>
|
||||
/// 默认设置是多库 <br/>
|
||||
/// 公共表无需区分 直接使用主库 各自业务在各自库中 <br/>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class MultiTenantAttribute : Attribute
|
||||
{
|
||||
public MultiTenantAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public MultiTenantAttribute(TenantTypeEnum tenantType)
|
||||
{
|
||||
TenantType = tenantType;
|
||||
}
|
||||
|
||||
|
||||
public TenantTypeEnum TenantType { get; set; } = TenantTypeEnum.Db;
|
||||
}
|
|
@ -20,4 +20,10 @@ public enum TenantTypeEnum
|
|||
/// </summary>
|
||||
[Description("库隔离")]
|
||||
Db = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 表隔离
|
||||
/// </summary>
|
||||
[Description("表隔离")]
|
||||
Tables = 3,
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
using Blog.Core.Common;
|
||||
using Blog.Core.Common.DB;
|
||||
using Blog.Core.IRepository.Base;
|
||||
using Blog.Core.Model;
|
||||
using Blog.Core.Model.Models;
|
||||
using Blog.Core.Model.Tenants;
|
||||
using Blog.Core.Repository.UnitOfWorks;
|
||||
using SqlSugar;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -8,11 +12,6 @@ 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
|
||||
{
|
||||
|
@ -45,7 +44,8 @@ namespace Blog.Core.Repository.Base
|
|||
}
|
||||
|
||||
//多租户
|
||||
if (typeof(TEntity).GetCustomAttribute<MultiTenantAttribute>() != null)
|
||||
var mta = typeof(TEntity).GetCustomAttribute<MultiTenantAttribute>();
|
||||
if (mta is { TenantType: TenantTypeEnum.Db })
|
||||
{
|
||||
//获取租户信息 租户信息可以提前缓存下来
|
||||
if (App.User is { TenantId: > 0 })
|
||||
|
|
73
Blog.Core.Tests/Repository_Test/OrmTest.cs
Normal file
73
Blog.Core.Tests/Repository_Test/OrmTest.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using Autofac;
|
||||
using Blog.Core.Common.Extensions;
|
||||
using Blog.Core.IRepository.Base;
|
||||
using Blog.Core.Model.Models;
|
||||
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
|
||||
using SqlSugar;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Blog.Core.Tests;
|
||||
|
||||
public class OrmTest
|
||||
{
|
||||
private readonly ITestOutputHelper _testOutputHelper;
|
||||
private readonly IBaseRepository<BlogArticle> _baseRepository;
|
||||
DI_Test dI_Test = new DI_Test();
|
||||
|
||||
public OrmTest(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_testOutputHelper = testOutputHelper;
|
||||
|
||||
var container = dI_Test.DICollections();
|
||||
|
||||
_baseRepository = container.Resolve<IBaseRepository<BlogArticle>>();
|
||||
_baseRepository.Db.Aop.OnLogExecuting = (sql, p) =>
|
||||
{
|
||||
_testOutputHelper.WriteLine("");
|
||||
_testOutputHelper.WriteLine("==================FullSql=====================", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】:" + sql });
|
||||
_testOutputHelper.WriteLine("【SQL语句】:" + sql);
|
||||
_testOutputHelper.WriteLine(GetParas(p));
|
||||
_testOutputHelper.WriteLine("==============================================");
|
||||
_testOutputHelper.WriteLine("");
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetParas(SugarParameter[] pars)
|
||||
{
|
||||
string key = "【SQL参数】:";
|
||||
foreach (var param in pars)
|
||||
{
|
||||
key += $"{param.ParameterName}:{param.Value}\n";
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiTables()
|
||||
{
|
||||
var sql = _baseRepository.Db.Queryable<BlogArticle>()
|
||||
.AS($@"{nameof(BlogArticle)}_TenantA")
|
||||
.ToSqlString();
|
||||
//_testOutputHelper.WriteLine(sql);
|
||||
|
||||
_baseRepository.Db.MappingTables.Add(nameof(BlogArticle), $@"{nameof(BlogArticle)}_TenantA");
|
||||
|
||||
var query = _baseRepository.Db.Queryable<BlogArticle>()
|
||||
.LeftJoin<BlogArticleComment>((a, c) => a.bID == c.bID);
|
||||
// query.QueryBuilder.AsTables.AddOrModify(nameof(BlogArticle), $@"{nameof(BlogArticle)}_TenantA");
|
||||
//query.QueryBuilder.AsTables.AddOrModify(nameof(BlogArticleComment), $@"{nameof(BlogArticleComment)}_TenantA");
|
||||
// query.QueryBuilder.AsTables.AddOrModify(nameof(BlogArticleComment), $@"{nameof(BlogArticleComment)}_TenantA");
|
||||
// query.QueryBuilder.AsTables.AddOrModify(nameof(SysUserInfo), $@"{nameof(SysUserInfo)}_TenantA");
|
||||
|
||||
|
||||
sql = query.ToSqlString();
|
||||
|
||||
_testOutputHelper.WriteLine(sql);
|
||||
|
||||
sql = _baseRepository.Db.Deleteable<BlogArticle>().ToSqlString();
|
||||
_testOutputHelper.WriteLine(sql);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user