Files
tnb.server/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs
2023-11-06 19:35:59 +08:00

452 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Concurrent;
using JNPF;
using JNPF.Common.Core.Manager;
using JNPF.Common.Extension;
using JNPF.DependencyInjection;
using SqlSugar;
using Tnb.Core;
using Tnb.Vengine.Domain;
namespace Tnb.Vengine.DataAccess;
/// <summary>
///
/// </summary>
public class DataAccess : IDataAccess, ITransient, IDisposable
{
private const int MAX_PAGE_SIZE = 1000;
private static ISqlSugarClient? _db;
protected ISqlSugarClient Db
{
get
{
if (_db == null)
{
ConnectionStringsOptions conn = App.GetConfig<ConnectionStringsOptions>("ConnectionStrings", true);
//var dbType = App.Configuration.GetConnectionString("DbType");
//var dbConn = App.Configuration.GetConnectionString("DbConn");
_db = SugarHelper.CreateSugarClient(conn.ConfigId, conn.DBType, conn.ConnectString);
}
return _db;
}
}
private readonly IUserManager _user;
/// <summary>
/// 全局缓存
/// </summary>
private static ConcurrentDictionary<string, ISqlSugarClient> DbCache = new ConcurrentDictionary<string, ISqlSugarClient>();
/// <summary>
/// 构造
/// </summary>
public DataAccess(IUserManager currentUser)
{
_user = currentUser;
}
/// <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 = GetVmodelLink(dbCode);
ThrowIf.IsNull(dblink, $"没有此数据库{dbCode}连接信息");
var sugar = SugarHelper.CreateSugarClient(dblink.dbCode, dblink.dbType, dblink.dbConnection);
if (sugar.Ado.IsValidConnection())
{
DbCache[dbCode] = sugar;
}
else
{
sugar.Dispose();
throw new Exception($"无法连接到数据库{dbCode}");
}
return DbCache[dbCode];
}
/// <summary>
/// 获取 DbLink
/// </summary>
public VmodelLink GetVmodelLink(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);
ThrowIf.IsNull(vm, $"找不到id={id}的模型");
if (loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
return vm;
}
/// <summary>
/// 获取 Vmodel, 为空时不抛异常
/// </summary>
public async Task<Vmodel?> TryGetVmodelAsync(string areaCode, string vmCode, bool loadNavigate = false)
{
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.areaCode == areaCode && a.vmCode == vmCode && a.deleted == 0);
if (vm != null && loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
return vm;
}
/// <summary>
/// 获取 Vmodel, 为空时抛异常
/// </summary>
public async Task<Vmodel> GetVmodelAsync(string areaCode, string vmCode, bool loadNavigate = false)
{
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.areaCode == areaCode && a.vmCode == vmCode && a.deleted == 0);
ThrowIf.IsNull(vm, $"找不到areaCode={areaCode}, 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();
var vmids = vm.navProps.Select(a => a.vmid).Distinct().ToList();
var ls = await Db.Queryable<Vmodel>().Where(a => vmids.Contains(a.id)).ToListAsync();
var navs = ls.ToDictionary(a => a.id);
foreach (var navProp in vm.navProps)
{
navProp.naviModel = (Vmodel)navs.GetOrDefault(navProp.vmid);
//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, VmListInput 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);
//if (joins.Count > 0)
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, CSharpTypeName = prop.csType }));
wType = WhereType.Or;
}
wheres.Add(new ConditionalCollections() { ConditionalList = lsCondition });
}
//处理查询参数
//if (wheres.Count > 0)
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 CombineOutputAsync(vm, new DObject(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> CombineOutputAsync(Vmodel vm, DObject 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)
{
NestedOutput(vm, src, ret, prop);
}
else if (prop.navType == eNavigateType.OneToMany)
{
await NestedOneToManyAsync(vm, src, ret, prop, selProps);
}
else if (prop.navType == eNavigateType.ManyToMany)
{
if (!ret.ContainsKey(prop.navCode))
{
ret.Add(prop.navCode, new List<DObject>());
}
}
}
}
return ret;
}
/// <summary>
/// 将一对一的关联表字段嵌入到返回值中
/// </summary>
private void NestedOutput(Vmodel vm, DObject src, DObject ret, VmSelectProp prop)
{
// 以 nav_prop的形式返回
var key = prop.navCode + "_" + prop.code;
ret.Add(key, src[key]);
// 以 nav.prop的形式返回
//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]);
//}
}
private async Task NestedOneToManyAsync(Vmodel vm, DObject src, DObject ret, VmSelectProp prop, List<VmSelectProp> selProps)
{
// 在返回值中增加导航属性
if (ret.ContainsKey(prop.navCode))
{
return;
}
ret.Add(prop.navCode, new List<DObject>());
// 找到导航属性的配置
var navCfg = vm.navProps.First(a => a.code == prop.navCode);
if (navCfg != null && navCfg.naviModel != null && src.ContainsKey(navCfg.refField) && navCfg.refCode == VmSelectProp.MAIN_ALIES)
{
VmListInput input = new VmListInput();
var fkProp = navCfg.naviModel.FieldCodeToPropCode(navCfg.fkField);
if (!string.IsNullOrEmpty(fkProp))
{
input.q = new DObject(fkProp, src[navCfg.refField]);
input.o = string.Join(',', selProps.Where(a => a.navCode == prop.navCode).Select(a => a.code));
ret[prop.navCode] = (await QueryDataAsync(navCfg.naviModel, input)).items;
}
}
}
/// <summary>
/// 新增数据 默认方法
/// </summary>
public async Task<dynamic> CreateDataAsync(Vmodel vm, VmCreateInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
ThrowIf.When(input.data == null && input.items == null, "新增数据时data和items不可同时为空");
//新增一条数据
if (input.data != null)
{
var pkey = vm.GetPrimary();
var model = vm.ToCreateEntity(input.data, _user);
if (pkey.csType == "int" || pkey.csType == "long")
{
var id = await db.Insertable(model).AS(vm.tableName).ExecuteReturnBigIdentityAsync();
if ((long)input.data[pkey.code] != id) input.data[pkey.code] = id;
}
else
{
await db.Insertable(model).AS(vm.tableName).ExecuteCommandAsync();
}
return input.data;
}
//批量新增数据
else
{
var pkey = vm.GetPrimary();
List<DObject> lst = new List<DObject>();
foreach (var item in input.items!)
{
lst.Add(vm.ToCreateEntity(item, _user));
}
if (pkey.csType == "int")
{
var ids = await db.Insertable(lst).AS(vm.tableName).ExecuteReturnPkListAsync<int>();
for (int i = 0; i < input.items.Count; i++)
{
input.items[i][pkey.code] = ids[i];
}
}
else if (pkey.csType == "long")
{
var ids = await db.Insertable(lst).AS(vm.tableName).ExecuteReturnPkListAsync<long>();
for (int i = 0; i < input.items.Count; i++)
{
input.items[i][pkey.code] = ids[i];
}
}
else
{
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.ToUpdateEntity(input.data, _user);
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.ToUpdateEntity(item, _user);
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;
}
}