🎨 增加多租户-分库方案

1.自动初始化维护租户库
2.多租户库种子数据维护

分库方案 TenantByDbController
This commit is contained in:
Lemon.NoCry 2023-02-18 01:17:19 +08:00
parent 1cc3ef25fa
commit 296201998d
22 changed files with 520 additions and 93 deletions

1
.gitignore vendored
View File

@ -355,3 +355,4 @@ Blog.Core/WMBlog.db
Blog.Core/Blog.Core*.xml
Blog.Core.Api/WMBlog.db
Blog.Core.Api/wwwroot/ui/
*.db

View File

@ -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>
广告类

View File

@ -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/>

View 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);
}
}

View File

@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Mvc;
namespace Blog.Core.Api.Controllers.Tenant;
/// <summary>
/// 多租户测试
/// 多租户-Id方案 测试
/// </summary>
[Produces("application/json")]
[Route("api/Tenant/ById")]

View File

@ -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; });
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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;

View 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
},
};
}
}

View File

@ -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);
}
}
}
}
}
}

View 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);
}
}

View File

@ -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"),
},
};
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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;

View 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; }
}

View File

@ -1,4 +1,4 @@
using Blog.Core.Model.CustomEnums;
using Blog.Core.Model.Tenants;
using SqlSugar;
namespace Blog.Core.Model.Models;

View File

@ -1,6 +1,6 @@
using SqlSugar;
namespace Blog.Core.Model.Models.RootTkey.Interface;
namespace Blog.Core.Model.Tenants;
/// <summary>
/// 租户模型接口

View File

@ -0,0 +1,12 @@
using System;
namespace Blog.Core.Model.Tenants;
/// <summary>
/// 标识 多租户-分库 的业务表 <br/>
/// 公共表无需区分 直接使用主库 各自业务在各自库中
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MultiTenantAttribute : Attribute
{
}

View File

@ -1,6 +1,6 @@
using System.ComponentModel;
namespace Blog.Core.Model.CustomEnums;
namespace Blog.Core.Model.Tenants;
/// <summary>
/// 租户隔离方案

View File

@ -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;
/*
* 1appsettings.json MutiDBEnabled节点为true
* 2IDMainDBEnabled也必须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);
}
}
}