using System.Collections.Concurrent; using System.Security.Cryptography.Xml; using JNPF; using JNPF.Common.Core.Manager; using JNPF.Common.Extension; using JNPF.DependencyInjection; using Microsoft.Extensions.Configuration; 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; 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 ConcurrentDictionary DbCache = new ConcurrentDictionary(); /// /// 构造 /// public DataAccess(IUserManager currentUser) { _user = currentUser; } /// /// 释放 /// public void Dispose() { foreach (var 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]; } 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]; } /// /// 获取 DbLink /// public VmodelLink GetVmodelLink(string dbCode) { var model = Db.Queryable().First(a => a.dbCode == dbCode); return model; } /// /// 获取 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) { Vmodel vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0); ThrowIf.IsNull(vm, $"找不到id={id}的模型"); if (loadNavigate) { await LoadVmodelNavigateAsync(vm); } 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(); var vmids = vm.navProps.Select(a => a.vmid).Distinct().ToList(); var ls = await Db.Queryable().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]; } } /// /// 查询数据 默认方法 /// public async Task QueryDataAsync(Vmodel vm, VmListInput input) { ISqlSugarClient db = GetSqlSugar(vm.dbCode); var query = db.Queryable().AS(vm.tableName, VmSelectProp.MAIN_ALIES); var selProps = vm.GetVmSelectProps(input.o); //处理导航属性联表 List joins = vm.GetJoinInfos(selProps); //if (joins.Count > 0) query.AddJoinInfo(joins); List wheres = vm.GetConditionalModels(input.q); if (!string.IsNullOrEmpty(input.k)) { var lsCondition = new List>(); 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 selects = vm.GetSelectModels(selProps); query.Select(selects); //查询数据 VmPagedOutput result = new(); List> 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; } /// /// 组装子模型对象 /// /// /// /// /// private async Task CombineOutputAsync(Vmodel vm, DObject src, List 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()); } } } } return ret; } /// /// 将一对一的关联表字段嵌入到返回值中 /// 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 selProps) { // 在返回值中增加导航属性 if (ret.ContainsKey(prop.navCode)) { return; } ret.Add(prop.navCode, new List()); // 找到导航属性的配置 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; } } } /// /// 新增数据 默认方法 /// 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) { 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 lst = new List(); 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(); 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(); 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); 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 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; } /// /// 删除数据 默认方法 /// public async Task DeleteDataAsync(Vmodel vm, VmDeleteInput input) { ISqlSugarClient db = GetSqlSugar(vm.dbCode); var 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; } }