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
{
///
/// 验证属性值是否符合数据库字段类型要求
///
/// 属性信息
/// 待验证的值
///
/// 元组包含三个值:
/// Item1 - 验证是否通过(true/false)
/// Item2 - 验证失败时的错误信息
/// Item3 - 原始输入值
///
///
/// 支持验证以下数据库类型:
/// - 整数类型(Int/BigInt)
/// - 日期时间类型(DateTime/Date等)
/// - 浮点类型(Float/Double/Decimal)
/// - GUID类型(UniqueIdentifier)
/// - 字符串类型(VarChar/NVarChar等)的长度限制
///
public static (bool, string, object) ValidationVal(this PropertyInfo propertyInfo, object value)
{
string dbType = "";
SugarColumn sugarColumn = null;
if (propertyInfo != null)
{
sugarColumn = propertyInfo.GetCustomAttribute();
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);
}
///
/// 验证属性值是否符合数据库类型要求
///
/// 要验证的属性信息
/// 要验证的值数组
/// 返回验证结果列表,每个结果包含是否成功、错误信息和验证后的值
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 ProperWithDbType = new Dictionary() {
{ 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}
};
///
/// 获取属性对应的数据库类型
///
/// 属性信息
/// 如果找到匹配则返回对应的数据库类型,否则返回默认的NVarChar类型
public static string GetProperWithDbType(this PropertyInfo propertyInfo)
{
bool result = ProperWithDbType.TryGetValue(propertyInfo.PropertyType, out string value);
if (result)
{
return value;
}
return SqlDbTypeName.NVarChar;
}
///
/// 验证字典中的字段是否匹配实体属性,并进行必要的清理和验证
///
/// 实体类型信息
/// 待验证的字段字典
/// 是否移除主键字段
/// 实体属性集合
/// 需要忽略的字段名数组
/// 验证通过返回空字符串,否则返回错误信息
///
/// 1. 移除字典中不存在的实体字段
/// 2. 根据配置决定是否移除主键字段
/// 3. 检查必填字段是否为空
/// 4. 将空字符串值转为null
///
public static string ValidateDicInEntity(this Type typeinfo, Dictionary 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();
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;
}
///
/// 验证字典列表中的字段是否与实体类型属性匹配
///
/// 实体类型
/// 要验证的字典列表
/// 是否移除不匹配的键
/// 要忽略的字段名数组
/// 返回验证结果消息,若为空表示验证通过
public static string ValidateDicInEntity(this Type typeinfo, List> dicList, bool removerKey, string[] ignoreFields = null)
{
PropertyInfo[] propertyInfo = typeinfo.GetProperties();
string reslutMsg = string.Empty;
foreach (Dictionary dic in dicList)
{
reslutMsg = typeinfo.ValidateDicInEntity(dic, removerKey, propertyInfo, ignoreFields);
if (!string.IsNullOrEmpty(reslutMsg))
return reslutMsg;
}
return reslutMsg;
}
///
/// 获取指定类型的键名称
///
/// 要获取键名称的类型
/// 返回该类型的键名称
public static string GetKeyName(this Type typeinfo)
{
return typeinfo.GetProperties().GetKeyName();
}
///
/// 从属性集合中获取主键属性的名称
///
/// 要搜索的属性集合
/// 如果找到主键属性则返回其名称,否则返回null
/// 通过查找带有[SugarColumn(IsPrimaryKey = true)]特性的属性来确定主键
public static string GetKeyName(this PropertyInfo[] properties)
{
foreach (PropertyInfo property in properties)
{
SugarColumn sugarColumn = property.GetCustomAttribute();
if (sugarColumn.IsPrimaryKey)
return property.Name;
}
return null;
}
///
/// 获取指定类型的主键属性
///
/// 要检查的类型
/// 如果找到标记为[SugarColumn(IsPrimaryKey = true)]的属性则返回该属性,否则返回null
public static PropertyInfo GetKeyProperty(this Type typeinfo)
{
PropertyInfo[] properties = typeinfo.GetProperties();
foreach (PropertyInfo property in properties)
{
SugarColumn sugarColumn = property.GetCustomAttribute();
if (sugarColumn?.IsPrimaryKey ?? false)
{
return property;
}
}
return null;
}
///
/// 获取类型的导航属性详细类型
///
/// 要检查的类型
///
/// 如果找到OneToOne导航属性则返回属性类型,
/// 如果是其他导航类型则返回泛型参数类型,
/// 未找到导航属性则返回null
///
public static Type GetDetailType(this Type typeinfo)
{
PropertyInfo[] properties = typeinfo.GetProperties();
foreach (PropertyInfo property in properties)
{
Navigate? navigate = property.GetCustomAttribute();
if (navigate is not null)
{
if (navigate.GetNavigateType() == NavigateType.OneToOne)
return property.PropertyType;
else
return property.PropertyType.GenericTypeArguments[0];
}
}
return null;
}
///
/// 通过实体类型获取其主键属性名称
///
/// 实体类型
/// 主键属性名称,如果未找到则返回null
///
/// 该方法通过查找实体类型上标记了[Navigate]特性的属性来获取主键名称
///
public static string GetMainIdByDetail(this Type typeinfo)
{
PropertyInfo[] properties = typeinfo.GetProperties();
foreach (PropertyInfo property in properties)
{
Navigate? navigate = property.GetCustomAttribute();
if (navigate is not null)
{
return navigate.GetName();
}
}
return null;
}
///
/// 为实体对象设置指定名称的属性值
///
/// 实体类型
/// 实体类型信息
/// 目标实体对象
/// 要设置的属性值
/// 属性名称
public static void SetDetailId(this Type typeinfo, T enetiy, object id, string name)
{
PropertyInfo property = typeinfo.GetProperty(name);
if (property != null)
{
property.SetValue(enetiy, id);
}
}
///
/// 获取指定类型中标记了[Navigate]特性的属性
///
/// 要搜索的目标类型
/// 找到的PropertyInfo对象,若未找到则返回null
public static PropertyInfo? GetNavigatePro(this Type typeinfo)
{
PropertyInfo[] properties = typeinfo.GetProperties();
foreach (PropertyInfo property in properties)
{
Navigate? navigate = property.GetCustomAttribute();
if (navigate is not null)
{
return property;
}
}
return null;
}
///
/// 获取指定对象的属性值
///
/// 对象类型
/// 类型信息
/// 目标对象
/// 属性名称
/// 属性值,如果属性不存在则返回null
public static object GetPropertyValue(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;
}
}
}