Files
tnb.server/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs
2023-08-15 11:41:49 +08:00

423 lines
12 KiB
C#

/////////////////////////////////////////////////////////////////////////////////
// 宁波拓通e智造平台 ToTong Next Builder //
// https://git.tuotong-tech.com/tnb/tnb.server //
/////////////////////////////////////////////////////////////////////////////////
using System.Collections.Concurrent;
using JNPF;
using JNPF.DependencyInjection;
using Mapster;
using SqlSugar;
using Tnb.Vengine.Domain;
namespace Tnb.Vengine.DataAccess;
/// <summary>
///
/// </summary>
public class DataAccess : IDataAccess, ITransient, IDisposable
{
const int MAX_PAGE_SIZE = 1000;
private ISqlSugarClient? sugar;
protected ISqlSugarClient Db
{
get
{
if (sugar == null)
{
ConnectionStringsOptions conn = App.GetConfig<ConnectionStringsOptions>("ConnectionStrings", true);
//var DBType = (DbType)Enum.Parse(typeof(DbType), conn.DBType);
sugar = new SqlSugarScope(new ConnectionConfig
{
ConnectionString = conn.ConnectString,
DbType = conn.DBType.Adapt<DbType>(),
IsAutoCloseConnection = true,
ConfigId = conn.ConfigId,
InitKeyType = InitKeyType.Attribute,
MoreSettings = new ConnMoreSettings()
{
IsAutoRemoveDataCache = true, // 自动清理缓存
IsAutoToUpper = false,
PgSqlIsAutoToLower = false,
DisableNvarchar = true
},
}, SugarHelper.ConfigSugar);
}
return sugar;
}
}
/// <summary>
/// 全局缓存
/// </summary>
static ConcurrentDictionary<string, ISqlSugarClient> DbCache = new ConcurrentDictionary<string, ISqlSugarClient>();
/// <summary>
/// 构造
/// </summary>
public DataAccess()
{
}
/// <summary>
/// 释放
/// </summary>
public void Dispose()
{
foreach (var item in DbCache)
{
item.Value.Dispose();
}
DbCache.Clear();
}
/// <summary>
/// 获取 ISqlSugarClient
/// </summary>
public ISqlSugarClient GetSqlSugar(string? dbCode = null)
{
if (string.IsNullOrEmpty(dbCode) || dbCode == DbConsts.DefaultDbCode)
{
return Db;
}
if (DbCache.ContainsKey(dbCode))
{
return DbCache[dbCode];
}
var dblink = GetVmLink(dbCode);
if (dblink == null)
{
throw new Exception($"没有此数据库{dbCode}连接信息");
}
var dbType = Enum.Parse<DbType>(dblink.dbType.ToString(), true);
var sugar = new SqlSugarScope(new ConnectionConfig
{
ConnectionString = dblink.dbConnection,
DbType = dbType,
IsAutoCloseConnection = true,
ConfigId = dblink.dbCode,
InitKeyType = InitKeyType.Attribute,
MoreSettings = new ConnMoreSettings()
{
IsAutoRemoveDataCache = true, // 自动清理缓存
IsAutoToUpper = false,
PgSqlIsAutoToLower = false,
DisableNvarchar = true
},
}, SugarHelper.ConfigSugar);
if (sugar.Ado.IsValidConnection())
{
DbCache[dbCode] = sugar;
}
else
{
sugar.Dispose();
throw new Exception($"无法连接到数据库{dbCode}");
}
return DbCache[dbCode];
}
/// <summary>
/// 获取 DbLink
/// </summary>
public VmodelLink GetVmLink(string dbCode)
{
var model = Db.Queryable<VmodelLink>().First(a => a.dbCode == dbCode);
return model;
}
/// <summary>
/// 获取 Vmodel, 为空时不抛异常
/// </summary>
public async Task<Vmodel?> TryGetVmodelAsync(string id, bool loadNavigate = false)
{
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.id == id && a.deleted == 0);
if (vm != null && loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
return vm;
}
/// <summary>
/// 获取 Vmodel, 为空时抛异常
/// </summary>
public async Task<Vmodel> GetVmodelAsync(string id, bool loadNavigate = false)
{
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.id == id && a.deleted == 0);
ArgumentNullException.ThrowIfNull(vm, $"找不到vmid={id}的模型");
if (loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
return vm;
}
/// <summary>
/// 获取 Vmodel, 为空时不抛异常
/// </summary>
public async Task<Vmodel?> TryGetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
{
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
if (vm != null && loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
return vm;
}
/// <summary>
/// 获取 Vmodel, 为空时抛异常
/// </summary>
public async Task<Vmodel> GetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
{
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
ArgumentNullException.ThrowIfNull(vm, $"找不到area={area}, vmCode={vmCode}的模型");
if (loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
return vm;
}
///// <summary>
///// 获取 Vmodel
///// </summary>
//public async Task<Vmodel?> GetVmodelAsync(string tableName, string? dbCode)
//{
// Vmodel vm = await _db.Queryable<Vmodel>().FirstAsync(a => a.tableName == tableName && a.dbCode == dbCode && a.deleted == 0);
// return vm;
//}
/// <summary>
/// 加载模型的导航属性
/// </summary>
/// <param name="vm"></param>
/// <returns></returns>
private async Task LoadVmodelNavigateAsync(Vmodel vm)
{
Dictionary<string, Vmodel> dictVm = new();
foreach (var navProp in vm.navProps)
{
if (!dictVm.ContainsKey(navProp.vmid))
{
var navModel = await GetVmodelAsync(navProp.vmid);
dictVm.Add(navProp.vmid, navModel);
}
navProp.naviModel = dictVm[navProp.vmid];
}
}
/// <summary>
/// 查询数据 默认方法
/// </summary>
public async Task<VmPagedOutput> QueryDataAsync(Vmodel vm, VmQueryInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
var query = db.Queryable<object>().AS(vm.tableName, VmSelectProp.MAIN_ALIES);
var selProps = vm.GetVmSelectProps(input.o);
//处理导航属性联表
List<JoinInfoParameter> joins = vm.GetJoinInfos(selProps);
query.AddJoinInfo(joins);
List<IConditionalModel> wheres = vm.GetConditionalModels(input.q);
if (!string.IsNullOrEmpty(input.k))
{
var lsCondition = new List<KeyValuePair<WhereType, ConditionalModel>>();
var wType = WhereType.And;
foreach (var prop in vm.dbProps.Where(a => a.fuzzy))
{
lsCondition.Add(new(wType, new ConditionalModel() { FieldName = prop.field, ConditionalType = ConditionalType.Like, FieldValue = input.k }));
wType = WhereType.Or;
}
wheres.Add(new ConditionalCollections() { ConditionalList = lsCondition });
}
//处理查询参数
query.Where(wheres);
if (!string.IsNullOrEmpty(input.sort))
{
query.OrderBy(input.sort);
}
//处理输出字段
List<SelectModel> selects = vm.GetSelectModels(selProps);
query.Select(selects);
//查询数据
VmPagedOutput result = new();
List<Dictionary<string, object>> ls = new();
int skip = input.pnum > 0 ? (input.pnum - 1) * input.psize : 0;
int take = input.psize == 0 ? MAX_PAGE_SIZE : input.psize;
if (input.pnum > 0) { result.total = await query.CountAsync(); }
ls = await query.Skip(skip).Take(take).ToDictionaryListAsync();
//组装输出对象
foreach (var data in ls)
{
DObject ret = await NestedOutputAsync(vm, data, selProps);
result.items.Add(ret);
}
return result;
}
/// <summary>
/// 组装子模型对象
/// </summary>
/// <param name="vm"></param>
/// <param name="src"></param>
/// <param name="selProps"></param>
/// <returns></returns>
private async Task<DObject> NestedOutputAsync(Vmodel vm, Dictionary<string, object> src, List<VmSelectProp> selProps)
{
DObject ret = new();
foreach (var prop in selProps)
{
if (prop.navType == eNavigateType.None || prop.navCode == VmSelectProp.MAIN_ALIES)
{
if (src.ContainsKey(prop.code))
{
ret.Add(prop.code, src[prop.code]);
}
}
else
{
if (prop.navType == eNavigateType.OneToOne)
{
var key = prop.navCode + "_" + prop.code;
ret.Add(key, src[key]);
//if (!ret.ContainsKey(prop.navCode))
//{
// ret.Add(prop.navCode, new DObject());
//}
//var key = prop.navCode + "_" + prop.code;
//if (src.ContainsKey(key))
//{
// ((DObject)ret[prop.navCode]).Add(prop.code, src[key]);
//}
}
else if (prop.navType == eNavigateType.OneToMany)
{
if (!ret.ContainsKey(prop.navCode))
{
ret.Add(prop.navCode, new List<DObject>());
}
var navProp = vm.navProps.First(a => a.code == prop.navCode);
if (navProp != null && navProp.naviModel != null && src.ContainsKey(navProp.refField))
{
VmQueryInput input = new VmQueryInput();
input.q = new DObject(navProp.refField, src[navProp.refField]);
input.o = string.Join(',', selProps.Where(a => a.navCode == prop.navCode).Select(a => a.code));
ret[prop.navCode] = (await QueryDataAsync(navProp.naviModel, input)).items;
}
}
else if (prop.navType == eNavigateType.ManyToMany)
{
if (!ret.ContainsKey(prop.navCode))
{
ret.Add(prop.navCode, new List<DObject>());
}
}
}
}
return ret;
}
/// <summary>
/// 新增数据 默认方法
/// </summary>
public async Task<dynamic> CreateDataAsync(Vmodel vm, VmCreateInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
int num = 0;
if (input.data != null)
{
var model = vm.PropToField(input.data);
num = await db.Insertable(model).AS(vm.tableName).ExecuteCommandAsync();
return input.data;
}
else if (input.items != null)
{
List<DObject> lst = new List<DObject>();
foreach (var item in input.items)
{
lst.Add(vm.PropToField(item));
}
num = await db.Insertable(lst).AS(vm.tableName).ExecuteCommandAsync();
return input.items;
}
else
{
input.data = vm.GetDefaultDObject();
var model = vm.PropToField(input.data);
num = await db.Insertable(model).AS(vm.tableName).ExecuteCommandAsync();
return input.data;
}
}
/// <summary>
/// 更新数据 默认方法
/// </summary>
public async Task<dynamic> UpdateDataAsync(Vmodel vm, VmUpdateInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
var pk = vm.GetPrimary();
int num = 0;
if (input.data != null)
{
var model = vm.PropToField(input.data);
if (!model.ContainsKey(pk.field))
{
throw new Exception($"更新数据时主键({pk.code})不可为空");
}
//if (!model.ContainsKey(pk.field) && input.id != null)
//{
// model.Add(pk.field, input.id);
//}
num = await db.Updateable(model).AS(vm.tableName).WhereColumns(pk.field).ExecuteCommandAsync();
}
else if (input.items != null)
{
List<DObject> lst = new();
foreach (var item in input.items)
{
var model = vm.PropToField(item);
if (model.ContainsKey(pk.field))
{
lst.Add(model);
}
}
num = await db.Updateable(lst).AS(vm.tableName).WhereColumns(pk.field).ExecuteCommandAsync();
}
return num;
}
/// <summary>
/// 删除数据 默认方法
/// </summary>
public async Task<int> DeleteDataAsync(Vmodel vm, VmDeleteInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
var pk = vm.GetPrimary();
int num = 0;
List<Dictionary<string, object>> ids = new();
if (input.id != null)
{
ids.Add(new DObject(pk.field, input.id));
}
else if (input.ids != null)
{
ids.AddRange(input.ids.Select(a => new DObject(pk.field, a)));
}
if (ids.Count > 0)
{
num = await db.Deleteable<object>().AS(vm.tableName).WhereColumns(ids).ExecuteCommandAsync();
}
return num;
}
}