427 lines
14 KiB
C#
427 lines
14 KiB
C#
using System.Collections.Concurrent;
|
||
using JNPF;
|
||
using JNPF.Common.Core.Manager;
|
||
using JNPF.DependencyInjection;
|
||
using Microsoft.Extensions.Configuration;
|
||
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 ISqlSugarClient? _db;
|
||
|
||
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 ConcurrentDictionary<string, ISqlSugarClient> DbCache = new ConcurrentDictionary<string, ISqlSugarClient>();
|
||
|
||
/// <summary>
|
||
/// 构造
|
||
/// </summary>
|
||
public DataAccess(IUserManager currentUser)
|
||
{
|
||
_user = currentUser;
|
||
}
|
||
|
||
/// <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 = 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];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取 DbLink
|
||
/// </summary>
|
||
public VmodelLink GetVmodelLink(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);
|
||
ThrowIf.IsNull(vm, $"找不到id={id}的模型");
|
||
if (loadNavigate)
|
||
{
|
||
await LoadVmodelNavigateAsync(vm);
|
||
}
|
||
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();
|
||
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);
|
||
//if (joins.Count > 0)
|
||
//{
|
||
//}
|
||
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 });
|
||
}
|
||
//处理查询参数
|
||
query.Where(wheres);
|
||
//if (wheres.Count > 0)
|
||
//{
|
||
//}
|
||
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, new DObject(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, DObject 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);
|
||
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<DObject> lst = new List<DObject>();
|
||
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<int>();
|
||
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<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, 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<DObject> 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;
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
} |