using SqlSugar;
|
using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations.Schema;
|
using System.Linq;
|
using System.Reflection;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEA_Core.Const;
|
using WIDESEA_Core.Helper;
|
|
namespace WIDESEA_Core.Utilities
|
{
|
public static class EntityProperties
|
{
|
|
/// <summary>
|
/// 验证属性值是否符合数据库字段类型要求
|
/// </summary>
|
/// <param name="propertyInfo">属性信息</param>
|
/// <param name="value">待验证的值</param>
|
/// <returns>
|
/// 元组包含三个值:
|
/// Item1 - 验证是否通过(true/false)
|
/// Item2 - 验证失败时的错误信息
|
/// Item3 - 原始输入值
|
/// </returns>
|
/// <remarks>
|
/// 支持验证以下数据库类型:
|
/// - 整数类型(Int/BigInt)
|
/// - 日期时间类型(DateTime/Date等)
|
/// - 浮点类型(Float/Double/Decimal)
|
/// - GUID类型(UniqueIdentifier)
|
/// - 字符串类型(VarChar/NVarChar等)的长度限制
|
/// </remarks>
|
public static (bool, string, object) ValidationVal(this PropertyInfo propertyInfo, object value)
|
{
|
string dbType = "";
|
SugarColumn sugarColumn = null;
|
if (propertyInfo != null)
|
{
|
sugarColumn = propertyInfo.GetCustomAttribute<SugarColumn>();
|
dbType = propertyInfo.PropertyType != null ? propertyInfo.GetProperWithDbType() : SqlDbTypeName.NVarChar;
|
}
|
dbType = dbType.ToLower();
|
string val = value?.ToString();
|
//验证长度
|
string reslutMsg = string.Empty;
|
if (dbType == SqlDbTypeName.Int)
|
{
|
if (!value.IsInt())
|
reslutMsg = "只能为有效整数";
|
} //2021.10.12增加属性校验long类型的支持
|
else if (dbType == SqlDbTypeName.BigInt)
|
{
|
if (!long.TryParse(val, out _))
|
{
|
reslutMsg = "只能为有效整数";
|
}
|
}
|
else if (dbType == SqlDbTypeName.DateTime
|
|| dbType == SqlDbTypeName.Date
|
|| dbType == SqlDbTypeName.SmallDateTime
|
|| dbType == SqlDbTypeName.SmallDate
|
)
|
{
|
if (!value.IsDate())
|
reslutMsg = "必须为日期格式";
|
}
|
else if (dbType == SqlDbTypeName.Float || dbType == SqlDbTypeName.Decimal || dbType == SqlDbTypeName.Double)
|
{
|
|
if (!val.IsNumber(null))
|
{
|
reslutMsg = "不是有效数字";
|
}
|
}
|
else if (dbType == SqlDbTypeName.UniqueIdentifier)
|
{
|
if (!val.IsGuid())
|
{
|
reslutMsg = propertyInfo.Name + "Guid不正确";
|
}
|
}
|
else if (propertyInfo != null
|
&& (dbType == SqlDbTypeName.VarChar
|
|| dbType == SqlDbTypeName.NVarChar
|
|| dbType == SqlDbTypeName.NChar
|
|| dbType == SqlDbTypeName.Char
|
|| dbType == SqlDbTypeName.Text))
|
{
|
|
//默认nvarchar(max) 、text 长度不能超过20000
|
if (val.Length > 200000)
|
{
|
reslutMsg = $"字符长度最多【200000】";
|
}
|
else
|
{
|
int length = sugarColumn.Length;
|
if (length == 0) { return (true, null, value); }
|
//判断双字节与单字段
|
else if (length < 8000 &&
|
((dbType.Substring(0, 1) != "n"
|
&& Encoding.UTF8.GetBytes(val.ToCharArray()).Length > length)
|
|| val.Length > length)
|
)
|
{
|
reslutMsg = $"最多只能【{length}】个字符。";
|
}
|
}
|
}
|
if (!string.IsNullOrEmpty(reslutMsg) && propertyInfo != null)
|
{
|
reslutMsg = sugarColumn.ColumnDescription + reslutMsg;
|
}
|
return (reslutMsg == "" ? true : false, reslutMsg, value);
|
}
|
|
/// <summary>
|
/// 验证属性值是否符合数据库类型要求
|
/// </summary>
|
/// <param name="propertyInfo">要验证的属性信息</param>
|
/// <param name="values">要验证的值数组</param>
|
/// <returns>返回验证结果列表,每个结果包含是否成功、错误信息和验证后的值</returns>
|
public static List<(bool, string, object)> ValidationValueForDbType(this PropertyInfo propertyInfo, params object[] values)
|
{
|
List<(bool, string, object)> result = new List<(bool, string, object)>();
|
foreach (object value in values)
|
{
|
result.Add(propertyInfo.ValidationVal(value));
|
}
|
return result;
|
}
|
|
private static readonly Dictionary<Type, string> ProperWithDbType = new Dictionary<Type, string>() {
|
{ typeof(string), SqlDbTypeName.NVarChar },
|
{ typeof(DateTime), SqlDbTypeName.DateTime},
|
{ typeof(long), SqlDbTypeName.BigInt },
|
{ typeof(int), SqlDbTypeName.Int},
|
{ typeof(decimal), SqlDbTypeName.Decimal },
|
{ typeof(float), SqlDbTypeName.Float },
|
{ typeof(double), SqlDbTypeName.Double },
|
{ typeof(byte), SqlDbTypeName.Int },//类型待完
|
{ typeof(Guid), SqlDbTypeName.UniqueIdentifier}
|
};
|
|
/// <summary>
|
/// 获取属性对应的数据库类型
|
/// </summary>
|
/// <param name="propertyInfo">属性信息</param>
|
/// <returns>如果找到匹配则返回对应的数据库类型,否则返回默认的NVarChar类型</returns>
|
public static string GetProperWithDbType(this PropertyInfo propertyInfo)
|
{
|
bool result = ProperWithDbType.TryGetValue(propertyInfo.PropertyType, out string value);
|
if (result)
|
{
|
return value;
|
}
|
return SqlDbTypeName.NVarChar;
|
}
|
|
/// <summary>
|
/// 验证字典中的字段是否匹配实体属性,并进行必要的清理和验证
|
/// </summary>
|
/// <param name="typeinfo">实体类型信息</param>
|
/// <param name="dic">待验证的字段字典</param>
|
/// <param name="removerKey">是否移除主键字段</param>
|
/// <param name="propertyInfo">实体属性集合</param>
|
/// <param name="ignoreFields">需要忽略的字段名数组</param>
|
/// <returns>验证通过返回空字符串,否则返回错误信息</returns>
|
/// <remarks>
|
/// 1. 移除字典中不存在的实体字段
|
/// 2. 根据配置决定是否移除主键字段
|
/// 3. 检查必填字段是否为空
|
/// 4. 将空字符串值转为null
|
/// </remarks>
|
public static string ValidateDicInEntity(this Type typeinfo, Dictionary<string, object> dic, bool removerKey, PropertyInfo[] propertyInfo, string[] ignoreFields = null)
|
{
|
if (dic == null || dic.Count == 0) { return "参数无效"; }
|
|
// 不存在的字段直接移除
|
dic.Where(x => !propertyInfo.Any(p => p.Name == x.Key.FirstLetterToUpper())).Select(s => s.Key).ToList().ForEach(f =>
|
{
|
dic.Remove(f);
|
});
|
|
string keyName = typeinfo.GetKeyName();
|
//移除主键
|
if (removerKey)
|
dic.Remove(keyName);
|
//else
|
//{
|
// if (!dic.ContainsKey(keyName))
|
// return "请传入主键参数";
|
//}
|
|
foreach (PropertyInfo property in propertyInfo)
|
{
|
SugarColumn sugarColumn = property.GetCustomAttribute<SugarColumn>();
|
if (sugarColumn == null)
|
return "请配置SugarColumn属性";
|
//忽略与主键的字段不做验证
|
if (property.Name == keyName.FirstLetterToUpper() || (ignoreFields != null && ignoreFields.Contains(property.Name)) || sugarColumn.IsOnlyIgnoreInsert || sugarColumn.IsOnlyIgnoreUpdate || sugarColumn.IsIgnore)
|
continue;
|
|
//不在编辑中的列,是否也要必填
|
if (!dic.ContainsKey(property.Name.FirstLetterToLower()))
|
{
|
if (!sugarColumn.IsNullable)
|
{
|
if (sugarColumn.DefaultValue == null)
|
return sugarColumn.ColumnDescription + "为必须提交项";
|
continue;
|
}
|
continue;
|
}
|
if(dic[property.Name.FirstLetterToLower()] != null)
|
{
|
string str = dic[property.Name.FirstLetterToLower()].ToString();
|
//将所有空值设置为null
|
if (str == string.Empty)
|
dic[property.Name.FirstLetterToLower()] = null;
|
}
|
|
}
|
return string.Empty;
|
}
|
|
/// <summary>
|
/// 验证字典列表中的字段是否与实体类型属性匹配
|
/// </summary>
|
/// <param name="typeinfo">实体类型</param>
|
/// <param name="dicList">要验证的字典列表</param>
|
/// <param name="removerKey">是否移除不匹配的键</param>
|
/// <param name="ignoreFields">要忽略的字段名数组</param>
|
/// <returns>返回验证结果消息,若为空表示验证通过</returns>
|
public static string ValidateDicInEntity(this Type typeinfo, List<Dictionary<string, object>> dicList, bool removerKey, string[] ignoreFields = null)
|
{
|
PropertyInfo[] propertyInfo = typeinfo.GetProperties();
|
string reslutMsg = string.Empty;
|
foreach (Dictionary<string, object> dic in dicList)
|
{
|
reslutMsg = typeinfo.ValidateDicInEntity(dic, removerKey, propertyInfo, ignoreFields);
|
if (!string.IsNullOrEmpty(reslutMsg))
|
return reslutMsg;
|
}
|
return reslutMsg;
|
}
|
|
/// <summary>
|
/// 获取指定类型的键名称
|
/// </summary>
|
/// <param name="typeinfo">要获取键名称的类型</param>
|
/// <returns>返回该类型的键名称</returns>
|
public static string GetKeyName(this Type typeinfo)
|
{
|
return typeinfo.GetProperties().GetKeyName();
|
}
|
|
/// <summary>
|
/// 从属性集合中获取主键属性的名称
|
/// </summary>
|
/// <param name="properties">要搜索的属性集合</param>
|
/// <returns>如果找到主键属性则返回其名称,否则返回null</returns>
|
/// <remarks>通过查找带有[SugarColumn(IsPrimaryKey = true)]特性的属性来确定主键</remarks>
|
public static string GetKeyName(this PropertyInfo[] properties)
|
{
|
foreach (PropertyInfo property in properties)
|
{
|
SugarColumn sugarColumn = property.GetCustomAttribute<SugarColumn>();
|
if (sugarColumn.IsPrimaryKey)
|
return property.Name;
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 获取指定类型的主键属性
|
/// </summary>
|
/// <param name="typeinfo">要检查的类型</param>
|
/// <returns>如果找到标记为[SugarColumn(IsPrimaryKey = true)]的属性则返回该属性,否则返回null</returns>
|
public static PropertyInfo GetKeyProperty(this Type typeinfo)
|
{
|
PropertyInfo[] properties = typeinfo.GetProperties();
|
foreach (PropertyInfo property in properties)
|
{
|
SugarColumn sugarColumn = property.GetCustomAttribute<SugarColumn>();
|
if (sugarColumn?.IsPrimaryKey ?? false)
|
{
|
return property;
|
}
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 获取类型的导航属性详细类型
|
/// </summary>
|
/// <param name="typeinfo">要检查的类型</param>
|
/// <returns>
|
/// 如果找到OneToOne导航属性则返回属性类型,
|
/// 如果是其他导航类型则返回泛型参数类型,
|
/// 未找到导航属性则返回null
|
/// </returns>
|
public static Type GetDetailType(this Type typeinfo)
|
{
|
PropertyInfo[] properties = typeinfo.GetProperties();
|
foreach (PropertyInfo property in properties)
|
{
|
Navigate? navigate = property.GetCustomAttribute<Navigate>();
|
if (navigate is not null)
|
{
|
if (navigate.GetNavigateType() == NavigateType.OneToOne)
|
return property.PropertyType;
|
else
|
return property.PropertyType.GenericTypeArguments[0];
|
}
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 通过实体类型获取其主键属性名称
|
/// </summary>
|
/// <param name="typeinfo">实体类型</param>
|
/// <returns>主键属性名称,如果未找到则返回null</returns>
|
/// <remarks>
|
/// 该方法通过查找实体类型上标记了[Navigate]特性的属性来获取主键名称
|
/// </remarks>
|
public static string GetMainIdByDetail(this Type typeinfo)
|
{
|
PropertyInfo[] properties = typeinfo.GetProperties();
|
foreach (PropertyInfo property in properties)
|
{
|
Navigate? navigate = property.GetCustomAttribute<Navigate>();
|
if (navigate is not null)
|
{
|
return navigate.GetName();
|
}
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 为实体对象设置指定名称的属性值
|
/// </summary>
|
/// <typeparam name="T">实体类型</typeparam>
|
/// <param name="typeinfo">实体类型信息</param>
|
/// <param name="enetiy">目标实体对象</param>
|
/// <param name="id">要设置的属性值</param>
|
/// <param name="name">属性名称</param>
|
public static void SetDetailId<T>(this Type typeinfo, T enetiy, object id, string name)
|
{
|
PropertyInfo property = typeinfo.GetProperty(name);
|
if (property != null)
|
{
|
property.SetValue(enetiy, id);
|
}
|
}
|
|
/// <summary>
|
/// 获取指定类型中标记了[Navigate]特性的属性
|
/// </summary>
|
/// <param name="typeinfo">要搜索的目标类型</param>
|
/// <returns>找到的PropertyInfo对象,若未找到则返回null</returns>
|
public static PropertyInfo? GetNavigatePro(this Type typeinfo)
|
{
|
PropertyInfo[] properties = typeinfo.GetProperties();
|
foreach (PropertyInfo property in properties)
|
{
|
Navigate? navigate = property.GetCustomAttribute<Navigate>();
|
if (navigate is not null)
|
{
|
return property;
|
}
|
}
|
return null;
|
}
|
|
/// <summary>
|
/// 获取指定对象的属性值
|
/// </summary>
|
/// <typeparam name="T">对象类型</typeparam>
|
/// <param name="typeinfo">类型信息</param>
|
/// <param name="data">目标对象</param>
|
/// <param name="propertyName">属性名称</param>
|
/// <returns>属性值,如果属性不存在则返回null</returns>
|
public static object GetPropertyValue<T>(this Type typeinfo, T data, string propertyName)
|
{
|
if (typeinfo != typeof(T))
|
return null;
|
|
PropertyInfo? property = typeinfo.GetProperty(propertyName);
|
if (property != null)
|
{
|
return property.GetValue(data);
|
}
|
return null;
|
}
|
}
|
}
|