423 lines
14 KiB
C#
423 lines
14 KiB
C#
/////////////////////////////////////////////////////////////////////////////////
|
|
// 宁波拓通e智造平台 ToTong Next Builder //
|
|
// https://git.tuotong-tech.com/tnb/tnb.server //
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
using System.Collections.Concurrent;
|
|
using JNPF;
|
|
using JNPF.DependencyInjection;
|
|
using Mapster;
|
|
using SqlSugar;
|
|
using Tnb.VmodelEngine;
|
|
|
|
namespace Tnb.DataAccess;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class DataAccess : IDataAccess, ITransient, IDisposable
|
|
{
|
|
const int MAX_PAGE_SIZE = 1000;
|
|
private ISqlSugarClient? sugar;
|
|
protected ISqlSugarClient Db
|
|
{
|
|
get
|
|
{
|
|
if (sugar == null)
|
|
{
|
|
ConnectionStringsOptions conn = App.GetConfig<ConnectionStringsOptions>("ConnectionStrings", true);
|
|
//var DBType = (DbType)Enum.Parse(typeof(DbType), conn.DBType);
|
|
sugar = new SqlSugarScope(new ConnectionConfig
|
|
{
|
|
ConnectionString = conn.ConnectString,
|
|
DbType = conn.DBType.Adapt<DbType>(),
|
|
IsAutoCloseConnection = true,
|
|
ConfigId = conn.ConfigId,
|
|
InitKeyType = InitKeyType.Attribute,
|
|
MoreSettings = new ConnMoreSettings()
|
|
{
|
|
IsAutoRemoveDataCache = true, // 自动清理缓存
|
|
IsAutoToUpper = false,
|
|
PgSqlIsAutoToLower = false,
|
|
DisableNvarchar = true
|
|
},
|
|
}, SugarHelper.ConfigSugar);
|
|
}
|
|
return sugar;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 全局缓存
|
|
/// </summary>
|
|
static ConcurrentDictionary<string, ISqlSugarClient> DbCache = new ConcurrentDictionary<string, ISqlSugarClient>();
|
|
|
|
/// <summary>
|
|
/// 构造
|
|
/// </summary>
|
|
public DataAccess()
|
|
{
|
|
}
|
|
|
|
/// <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 = GetVmLink(dbCode);
|
|
if (dblink == null)
|
|
{
|
|
throw new Exception($"没有此数据库{dbCode}连接信息");
|
|
}
|
|
var dbType = Enum.Parse<DbType>(dblink.dbType.ToString(), true);
|
|
var sugar = new SqlSugarScope(new ConnectionConfig
|
|
{
|
|
ConnectionString = dblink.dbConnection,
|
|
DbType = dbType,
|
|
IsAutoCloseConnection = true,
|
|
ConfigId = dblink.dbCode,
|
|
InitKeyType = InitKeyType.Attribute,
|
|
MoreSettings = new ConnMoreSettings()
|
|
{
|
|
IsAutoRemoveDataCache = true, // 自动清理缓存
|
|
IsAutoToUpper = false,
|
|
PgSqlIsAutoToLower = false,
|
|
DisableNvarchar = true
|
|
},
|
|
}, SugarHelper.ConfigSugar);
|
|
if (sugar.Ado.IsValidConnection())
|
|
{
|
|
DbCache[dbCode] = sugar;
|
|
}
|
|
else
|
|
{
|
|
sugar.Dispose();
|
|
throw new Exception($"无法连接到数据库{dbCode}");
|
|
}
|
|
return DbCache[dbCode];
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取 DbLink
|
|
/// </summary>
|
|
public VmodelLink GetVmLink(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);
|
|
ArgumentNullException.ThrowIfNull(vm, $"找不到vmid={id}的模型");
|
|
if (loadNavigate)
|
|
{
|
|
await LoadVmodelNavigateAsync(vm);
|
|
}
|
|
return vm;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取 Vmodel, 为空时不抛异常
|
|
/// </summary>
|
|
public async Task<Vmodel?> TryGetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
|
|
{
|
|
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
|
|
if (vm != null && loadNavigate)
|
|
{
|
|
await LoadVmodelNavigateAsync(vm);
|
|
}
|
|
|
|
return vm;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取 Vmodel, 为空时抛异常
|
|
/// </summary>
|
|
public async Task<Vmodel> GetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
|
|
{
|
|
Vmodel vm = await Db.Queryable<Vmodel>().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
|
|
ArgumentNullException.ThrowIfNull(vm, $"找不到area={area}, 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();
|
|
foreach (var navProp in vm.navProps)
|
|
{
|
|
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)
|
|
{
|
|
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);
|
|
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 }));
|
|
wType = WhereType.Or;
|
|
}
|
|
wheres.Add(new ConditionalCollections() { ConditionalList = lsCondition });
|
|
}
|
|
//处理查询参数
|
|
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 NestedOutputAsync(vm, 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> NestedOutputAsync(Vmodel vm, Dictionary<string, object> 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)
|
|
{
|
|
var key = prop.navCode + "_" + prop.code;
|
|
ret.Add(key, src[key]);
|
|
//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]);
|
|
//}
|
|
}
|
|
else if (prop.navType == eNavigateType.OneToMany)
|
|
{
|
|
if (!ret.ContainsKey(prop.navCode))
|
|
{
|
|
ret.Add(prop.navCode, new List<DObject>());
|
|
}
|
|
var navProp = vm.navProps.First(a => a.code == prop.navCode);
|
|
if (navProp != null && navProp.naviModel != null && src.ContainsKey(navProp.refField))
|
|
{
|
|
VmQueryInput input = new VmQueryInput();
|
|
input.q = new DObject(navProp.refField, src[navProp.refField]);
|
|
input.o = string.Join(',', selProps.Where(a => a.navCode == prop.navCode).Select(a => a.code));
|
|
ret[prop.navCode] = (await QueryDataAsync(navProp.naviModel, input)).items;
|
|
}
|
|
}
|
|
else if (prop.navType == eNavigateType.ManyToMany)
|
|
{
|
|
if (!ret.ContainsKey(prop.navCode))
|
|
{
|
|
ret.Add(prop.navCode, new List<DObject>());
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 新增数据 默认方法
|
|
/// </summary>
|
|
public async Task<dynamic> CreateDataAsync(Vmodel vm, VmCreateInput input)
|
|
{
|
|
ISqlSugarClient db = GetSqlSugar(vm.dbCode);
|
|
int num = 0;
|
|
if (input.data != null)
|
|
{
|
|
var model = vm.PropToField(input.data);
|
|
num = await db.Insertable(model).AS(vm.tableName).ExecuteCommandAsync();
|
|
return input.data;
|
|
}
|
|
else if (input.items != null)
|
|
{
|
|
List<DObject> lst = new List<DObject>();
|
|
foreach (var item in input.items)
|
|
{
|
|
lst.Add(vm.PropToField(item));
|
|
}
|
|
num = 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.PropToField(input.data);
|
|
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.PropToField(item);
|
|
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;
|
|
}
|
|
|
|
}
|