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; /// /// /// 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("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; /// /// 全局缓存 /// private static readonly ConcurrentDictionary DbCache = new(); /// /// 构造 /// public DataAccess(IUserManager currentUser, ICacheManager cache) { _user = currentUser; _cache = cache; } /// /// 释放 /// public void Dispose() { foreach (KeyValuePair item in DbCache) { item.Value.Dispose(); } DbCache.Clear(); } /// /// 获取 ISqlSugarClient /// 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]; } /// /// 获取 DbLink /// public VmodelLink GetVmodelLink(string dbCode) { VmodelLink model = Db.Queryable().First(a => a.dbCode == dbCode); return model; } /// /// 获取 Vmodel 的缓存键 /// public string GetVmodelCacheKey(string id) { return $"tnb.vmodel:{id}"; } /// /// 获取 Vmodel, 为空时不抛异常 /// public async Task TryGetVmodelAsync(string id, bool loadNavigate = false) { Vmodel vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0); if (vm != null && loadNavigate) { await LoadVmodelNavigateAsync(vm); } return vm; } /// /// 获取 Vmodel, 为空时抛异常 /// public async Task GetVmodelAsync(string id, bool loadNavigate = false) { var key = GetVmodelCacheKey(id); var vm = await _cache.GetAsync(key); if (vm == null) { vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0); ThrowIf.IsNull(vm, $"找不到id={id}的模型"); if (loadNavigate) { await LoadVmodelNavigateAsync(vm); } await _cache.SetAsync(id, vm, TimeSpan.FromMinutes(10)); } return vm; } /// /// 获取 Vmodel, 为空时不抛异常 /// public async Task TryGetVmodelAsync(string areaCode, string vmCode, bool loadNavigate = false) { Vmodel vm = await Db.Queryable().FirstAsync(a => a.areaCode == areaCode && a.vmCode == vmCode && a.deleted == 0); if (vm != null && loadNavigate) { await LoadVmodelNavigateAsync(vm); } return vm; } /// /// 获取 Vmodel, 为空时抛异常 /// public async Task GetVmodelAsync(string areaCode, string vmCode, bool loadNavigate = false) { Vmodel vm = await Db.Queryable().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; } ///// ///// 获取 Vmodel ///// //public async Task GetVmodelAsync(string tableName, string? dbCode) //{ // Vmodel vm = await _db.Queryable().FirstAsync(a => a.tableName == tableName && a.dbCode == dbCode && a.deleted == 0); // return vm; //} /// /// 加载模型的导航属性 /// /// /// private async Task LoadVmodelNavigateAsync(Vmodel vm) { Dictionary dictVm = new(); List vmids = vm.navProps.Select(a => a.vmid).Distinct().ToList(); List ls = await Db.Queryable().Where(a => vmids.Contains(a.id)).ToListAsync(); Dictionary 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]; } } /// /// 查询数据 默认方法 /// public async Task QueryDataAsync(Vmodel vm, VmQueryInput input) { //var sw = Stopwatch.StartNew(); ISqlSugarClient db = GetSqlSugar(vm.dbCode); ISugarQueryable query = db.Queryable().AS(vm.tableName, VmQueryParser.MAIN_ALIES); VmQueryParser parser = new(this, vm, input); parser.ParseQueryInput(); await parser.LoadNavigateAsync(); // 处理导航属性联表 List joins = parser.GetJoinInfos(); _ = query.AddJoinInfo(joins); // 处理查询参数条件 List wheres = parser.GetConditionalModels(); wheres.Add(parser.GetKeywordConditional()); _ = query.Where(wheres); // 处理排序字段 _ = query.OrderBy(parser.GetOrderByModels()); //处理输出字段 List 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> 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; } /// /// 组装子模型对象 /// private async Task CombineOutputAsync(List> src, List 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 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()); // 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; // } //} } } } /// /// 新增数据 默认方法 /// public async Task CreateDataAsync(Vmodel vm, VmCreateInput 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 lst = new(); foreach (DObject item in input.items!) { lst.Add(vm.ToCreateEntity(item, _user)); } if (pkey.csType == "int") { List ids = await db.Insertable(lst).AS(vm.tableName).ExecuteReturnPkListAsync(); for (int i = 0; i < input.items.Count; i++) { input.items[i][pkey.code] = ids[i]; } } else if (pkey.csType == "long") { List ids = await db.Insertable(lst).AS(vm.tableName).ExecuteReturnPkListAsync(); 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; //} } /// /// 更新数据 默认方法 /// public async Task UpdateDataAsync(Vmodel vm, VmUpdateInput input) { ISqlSugarClient db = GetSqlSugar(vm.dbCode); VmDbProp pk = vm.GetPrimary(); int num = 0; //修改一条数据 if (input.data != null) { DObject 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 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; } /// /// 删除数据 默认方法 /// public async Task DeleteDataAsync(Vmodel vm, VmDeleteInput input) { ISqlSugarClient db = GetSqlSugar(vm.dbCode); VmDbProp pk = vm.GetPrimary(); int num = 0; List> 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().AS(vm.tableName).WhereColumns(ids).ExecuteCommandAsync(); } return num; } }