Files
tnb.server/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs

441 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.Common.Manager;
using JNPF.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
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;
private readonly ICacheManager _cache;
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 readonly ConcurrentDictionary<string, ISqlSugarClient> DbCache = new();
/// <summary>
/// 构造
/// </summary>
public DataAccess(IUserManager currentUser, ICacheManager cache)
{
_user = currentUser;
_cache = cache;
}
/// <summary>
/// 释放
/// </summary>
public void Dispose()
{
foreach (KeyValuePair<string, ISqlSugarClient> 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];
}
VmodelLink dblink = GetVmodelLink(dbCode);
ThrowIf.IsNull(dblink, $"没有此数据库{dbCode}连接信息");
ISqlSugarClient 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)
{
VmodelLink model = Db.Queryable<VmodelLink>().First(a => a.dbCode == dbCode);
return model;
}
/// <summary>
/// 获取 Vmodel 的缓存键
/// </summary>
public string GetVmodelCacheKey(string id)
{
return $"tnb.vmodel:{id}";
}
/// <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)
{
var key = GetVmodelCacheKey(id);
var vm = await _cache.GetAsync<Vmodel>(key);
if (vm == null)
{
vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.id == id && a.deleted == 0);
ThrowIf.IsNull(vm, $"找不到id={id}的模型");
if (loadNavigate)
{
await LoadVmodelNavigateAsync(vm);
}
await _cache.SetAsync(key, vm, TimeSpan.FromMinutes(10));
}
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();
List<string> vmids = vm.navProps.Select(a => a.vmid).Distinct().ToList();
List<Vmodel> ls = await Db.Queryable<Vmodel>().Where(a => vmids.Contains(a.id)).ToListAsync();
Dictionary<string, Vmodel> navs = ls.ToDictionary(a => a.id);
foreach (VmNavProp navProp in vm.navProps)
{
navProp.naviModel = 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, VmQueryInput input)
{
//var sw = Stopwatch.StartNew();
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
ISugarQueryable<object> query = db.Queryable<object>().AS(vm.tableName, VmQueryParser.MAIN_ALIES);
VmQueryParser parser = new(this, vm, input);
await parser.LoadNavigateAsync();
// 处理导航属性联表
List<JoinInfoParameter> joins = parser.GetJoinInfos();
_ = query.AddJoinInfo(joins);
// 处理查询参数条件
List<IConditionalModel> wheres = parser.GetConditionalModels();
wheres.Add(parser.GetKeywordConditional());
_ = query.Where(wheres);
// 处理排序字段
_ = query.OrderBy(parser.GetOrderByModels());
//处理输出字段
List<SelectModel> selects = parser.GetSelectModels();
_ = query.Select(selects);
//JNPF.Logging.Log.Debug($"解析查询参数耗时:{sw.ElapsedMilliseconds}ms");
//sw.Restart();
//查询数据
VmPagedOutput result = 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(); }
List<Dictionary<string, object>> ls = await query.Skip(skip).Take(take).ToDictionaryListAsync();
//JNPF.Logging.Log.Debug($"查询数据耗时:{sw.ElapsedMilliseconds}ms");
//sw.Restart();
for (int i = 0; i < ls.Count; i++)
{
result.items.Add(new DObject());
}
await CombineOutputAsync(ls, result.items, parser);
//JNPF.Logging.Log.Debug($"组装返回结果耗时:{sw.ElapsedMilliseconds}ms");
return result;
}
/// <summary>
/// 组装子模型对象
/// </summary>
private async Task CombineOutputAsync(List<Dictionary<string, object>> src, List<DObject> dest, VmQueryParser parser)
{
foreach (VmNavigate nav in parser.Navigates.Values)
{
// 加载主表字段
if (nav.path == VmQueryParser.MAIN_ALIES || nav.navConfig.navType == eNavigateType.None)
{
foreach (VmSelectProp prop in nav.selects)
{
for (int i = 0; i < src.Count; i++)
{
dest[i].Add(prop.code, src[i].GetOrDefault(prop.code));
}
}
}
else if (nav.navConfig.navType == eNavigateType.OneToOne)
{
foreach (VmSelectProp prop in nav.selects)
{
string key = prop.asName.Replace(VmQueryParser.PROP_SEPERATE, VmQueryParser.NAVI_SEPERATE);
for (int i = 0; i < src.Count; i++)
{
dest[i].Add(key, src[i].GetOrDefault(prop.asName));
}
}
}
else if (nav.navConfig.navType == eNavigateType.OneToMany)
{
object[] ids = new object[] { "in" };
ids = ids.Union(dest.Select(a => a.GetOrDefault(nav.navConfig.refProp)).Where(a => a != null)).ToArray();
VmQueryInput input = new()
{
q = new DObject(nav.navConfig.fkProp, ids)
};
nav.wheres.ForEach(a =>
{
input.q.Add(a.code, a.value);
});
input.o = string.Join(',', nav.selects.Select(a => a.code));
List<DObject> childs = (await QueryDataAsync(nav.navConfig.naviModel!, input)).items;
for (int i = 0; i < dest.Count; i++)
{
string? fkValue = dest[i][nav.navConfig.refProp]?.ToString();
dest[i][nav.pathCode] = childs.Where(a => a[nav.navConfig.fkProp]?.ToString() == fkValue).ToList();
}
//for (int i = 0; i < src.Count; i++)
//{
// //dest[i].Add(selectVm.pathCode, new List<DObject>());
// if (src[i].ContainsKey(refProp))
// {
// VmQueryInput input = new VmQueryInput();
// input.q = new DObject(fkProp, src[i][refProp]);
// nav.wheres.ForEach(a =>
// {
// input.q.Add(a.code, a.value);
// });
// input.o = string.Join(',', nav.selects.Select(a => a.code));
// dest[i][nav.pathCode] = (await QueryDataAsync(nav.navConfig.naviModel, input)).items;
// }
//}
}
}
}
/// <summary>
/// 新增数据 默认方法
/// </summary>
public async Task<dynamic> CreateDataAsync(Vmodel vm, VmEditInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
ThrowIf.When(input.data == null && input.items == null, "新增数据时data和items不可同时为空");
//新增一条数据
if (input.data != null)
{
VmDbProp pkey = vm.GetPrimary();
DObject model = vm.ToCreateEntity(input.data, _user);
if (pkey.csType is "int" or "long")
{
long 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
{
VmDbProp pkey = vm.GetPrimary();
List<DObject> lst = new();
foreach (DObject item in input.items!)
{
lst.Add(vm.ToCreateEntity(item, _user));
}
if (pkey.csType == "int")
{
List<int> 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")
{
List<long> 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, VmEditInput input)
{
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
VmDbProp pk = vm.GetPrimary();
int num = 0;
//修改一条数据
if (input.data != null)
{
DObject model = vm.ToUpdateEntity(input.data, _user);
ThrowIf.When(!model.ContainsKey(pk.field), $"更新数据时主键({pk.code})不可为空");
num = await db.Updateable(model).AS(vm.tableName).WhereColumns(pk.field).ExecuteCommandAsync();
}
//批量修改数据
else if (input.items != null)
{
List<DObject> lst = new();
foreach (DObject item in input.items)
{
DObject 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);
VmDbProp 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;
}
}