完善模型通用接口的无限层级一对一和单层级一对多
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using JNPF;
|
||||
using JNPF.Common.Core.Manager;
|
||||
using JNPF.Common.Extension;
|
||||
@@ -37,7 +38,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
/// <summary>
|
||||
/// 全局缓存
|
||||
/// </summary>
|
||||
private static ConcurrentDictionary<string, ISqlSugarClient> DbCache = new ConcurrentDictionary<string, ISqlSugarClient>();
|
||||
private static readonly ConcurrentDictionary<string, ISqlSugarClient> DbCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 构造
|
||||
@@ -52,7 +53,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var item in DbCache)
|
||||
foreach (KeyValuePair<string, ISqlSugarClient> item in DbCache)
|
||||
{
|
||||
item.Value.Dispose();
|
||||
}
|
||||
@@ -73,9 +74,9 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
return DbCache[dbCode];
|
||||
}
|
||||
|
||||
var dblink = GetVmodelLink(dbCode);
|
||||
VmodelLink dblink = GetVmodelLink(dbCode);
|
||||
ThrowIf.IsNull(dblink, $"没有此数据库{dbCode}连接信息");
|
||||
var sugar = SugarHelper.CreateSugarClient(dblink.dbCode, dblink.dbType, dblink.dbConnection);
|
||||
ISqlSugarClient sugar = SugarHelper.CreateSugarClient(dblink.dbCode, dblink.dbType, dblink.dbConnection);
|
||||
if (sugar.Ado.IsValidConnection())
|
||||
{
|
||||
DbCache[dbCode] = sugar;
|
||||
@@ -93,7 +94,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
/// </summary>
|
||||
public VmodelLink GetVmodelLink(string dbCode)
|
||||
{
|
||||
var model = Db.Queryable<VmodelLink>().First(a => a.dbCode == dbCode);
|
||||
VmodelLink model = Db.Queryable<VmodelLink>().First(a => a.dbCode == dbCode);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -170,12 +171,12 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
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)
|
||||
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 = (Vmodel)navs.GetOrDefault(navProp.vmid);
|
||||
navProp.naviModel = navs.GetOrDefault(navProp.vmid);
|
||||
//if (!dictVm.ContainsKey(navProp.vmid))
|
||||
//{
|
||||
// var navModel = await GetVmodelAsync(navProp.vmid);
|
||||
@@ -184,54 +185,50 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
//navProp.naviModel = dictVm[navProp.vmid];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询数据 默认方法
|
||||
/// </summary>
|
||||
public async Task<VmPagedOutput> QueryDataAsync(Vmodel vm, VmListInput input)
|
||||
public async Task<VmPagedOutput> QueryDataAsync(Vmodel vm, VmQueryInput input)
|
||||
{
|
||||
//var sw = Stopwatch.StartNew();
|
||||
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);
|
||||
}
|
||||
ISugarQueryable<object> query = db.Queryable<object>().AS(vm.tableName, VmQueryParser.MAIN_ALIES);
|
||||
VmQueryParser parser = new(this, vm, input);
|
||||
parser.ParseQueryInput();
|
||||
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 = vm.GetSelectModels(selProps);
|
||||
query.Select(selects);
|
||||
List<SelectModel> selects = parser.GetSelectModels();
|
||||
_ = query.Select(selects);
|
||||
//JNPF.Logging.Log.Debug($"解析查询参数耗时:{sw.ElapsedMilliseconds}ms");
|
||||
//sw.Restart();
|
||||
|
||||
//查询数据
|
||||
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)
|
||||
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++)
|
||||
{
|
||||
DObject ret = await CombineOutputAsync(vm, new DObject(data), selProps);
|
||||
result.items.Add(ret);
|
||||
result.items.Add(new DObject());
|
||||
}
|
||||
await CombineOutputAsync(ls, result.items, parser);
|
||||
//JNPF.Logging.Log.Debug($"组装返回结果耗时:{sw.ElapsedMilliseconds}ms");
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -239,86 +236,66 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
/// <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)
|
||||
private async Task CombineOutputAsync(List<Dictionary<string, object>> src, List<DObject> dest, VmQueryParser parser)
|
||||
{
|
||||
DObject ret = new();
|
||||
foreach (var prop in selProps)
|
||||
foreach (VmNavigate nav in parser.Navigates.Values)
|
||||
{
|
||||
// 加载主表字段
|
||||
if (prop.navType == eNavigateType.None || prop.navCode == VmSelectProp.MAIN_ALIES)
|
||||
if (nav.path == VmQueryParser.MAIN_ALIES || nav.navConfig.navType == eNavigateType.None)
|
||||
{
|
||||
if (src.ContainsKey(prop.code))
|
||||
foreach (VmSelectProp prop in nav.selects)
|
||||
{
|
||||
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))
|
||||
for (int i = 0; i < src.Count; i++)
|
||||
{
|
||||
ret.Add(prop.navCode, new List<DObject>());
|
||||
dest[i].Add(prop.code, src[i].GetOrDefault(prop.code));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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))
|
||||
else if (nav.navConfig.navType == eNavigateType.OneToOne)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,31 +310,34 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
//新增一条数据
|
||||
if (input.data != null)
|
||||
{
|
||||
var pkey = vm.GetPrimary();
|
||||
var model = vm.ToCreateEntity(input.data, _user);
|
||||
if (pkey.csType == "int" || pkey.csType == "long")
|
||||
VmDbProp pkey = vm.GetPrimary();
|
||||
DObject model = vm.ToCreateEntity(input.data, _user);
|
||||
if (pkey.csType is "int" or "long")
|
||||
{
|
||||
var id = await db.Insertable(model).AS(vm.tableName).ExecuteReturnBigIdentityAsync();
|
||||
if ((long)input.data[pkey.code] != id) input.data[pkey.code] = id;
|
||||
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();
|
||||
_ = 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!)
|
||||
VmDbProp pkey = vm.GetPrimary();
|
||||
List<DObject> lst = new();
|
||||
foreach (DObject 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>();
|
||||
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];
|
||||
@@ -365,7 +345,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
}
|
||||
else if (pkey.csType == "long")
|
||||
{
|
||||
var ids = await db.Insertable(lst).AS(vm.tableName).ExecuteReturnPkListAsync<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];
|
||||
@@ -373,7 +353,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
await db.Insertable(lst).AS(vm.tableName).ExecuteCommandAsync();
|
||||
_ = await db.Insertable(lst).AS(vm.tableName).ExecuteCommandAsync();
|
||||
}
|
||||
return input.items;
|
||||
}
|
||||
@@ -392,12 +372,12 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
public async Task<dynamic> UpdateDataAsync(Vmodel vm, VmUpdateInput input)
|
||||
{
|
||||
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
|
||||
var pk = vm.GetPrimary();
|
||||
VmDbProp pk = vm.GetPrimary();
|
||||
int num = 0;
|
||||
//修改一条数据
|
||||
if (input.data != null)
|
||||
{
|
||||
var model = vm.ToUpdateEntity(input.data, _user);
|
||||
DObject model = vm.ToUpdateEntity(input.data, _user);
|
||||
if (!model.ContainsKey(pk.field))
|
||||
{
|
||||
throw new Exception($"更新数据时主键({pk.code})不可为空");
|
||||
@@ -412,9 +392,9 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
else if (input.items != null)
|
||||
{
|
||||
List<DObject> lst = new();
|
||||
foreach (var item in input.items)
|
||||
foreach (DObject item in input.items)
|
||||
{
|
||||
var model = vm.ToUpdateEntity(item, _user);
|
||||
DObject model = vm.ToUpdateEntity(item, _user);
|
||||
if (model.ContainsKey(pk.field))
|
||||
{
|
||||
lst.Add(model);
|
||||
@@ -431,7 +411,7 @@ public class DataAccess : IDataAccess, ITransient, IDisposable
|
||||
public async Task<int> DeleteDataAsync(Vmodel vm, VmDeleteInput input)
|
||||
{
|
||||
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
|
||||
var pk = vm.GetPrimary();
|
||||
VmDbProp pk = vm.GetPrimary();
|
||||
int num = 0;
|
||||
List<Dictionary<string, object>> ids = new();
|
||||
if (input.id != null)
|
||||
|
||||
@@ -48,7 +48,7 @@ public interface IDataAccess : ITransient
|
||||
/// <summary>
|
||||
/// 查询数据 默认方法
|
||||
/// </summary>
|
||||
Task<VmPagedOutput> QueryDataAsync(Vmodel vm, VmListInput input);
|
||||
Task<VmPagedOutput> QueryDataAsync(Vmodel vm, VmQueryInput input);
|
||||
|
||||
//Task<dynamic> CreateDataAsync(VmCreateInput input);
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user