You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1356 lines
53 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Common.Const;
using Common.Extensions.Lambda;
using Common.Utilities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace Common.Extensions
{
public static class EntityProperties
{
public static string GetExpressionPropertyFirst<TEntity>(this Expression<Func<TEntity, object>> properties)
{
string[] arr = properties.GetExpressionProperty();
if (arr.Length > 0)
return arr[0];
return "";
}
/// <summary>
/// 获取对象里指定成员名称
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="properties"> 格式 Expression<Func<entityt, object>> exp = x => new { x.字段1, x.字段2 };或x=>x.Name</param>
/// <returns></returns>
public static string[] GetExpressionProperty<TEntity>(this Expression<Func<TEntity, object>> properties)
{
if (properties == null)
return new string[] { };
if (properties.Body is NewExpression)
return ((NewExpression)properties.Body).Members.Select(x => x.Name).ToArray();
if (properties.Body is MemberExpression)
return new string[] { ((MemberExpression)properties.Body).Member.Name };
if (properties.Body is UnaryExpression)
return new string[] { ((properties.Body as UnaryExpression).Operand as MemberExpression).Member.Name };
throw new Exception("未实现的表达式");
}
public static string ValidateHashInEntity(this Type typeinfo, Dictionary<string, object> dic)
{
return typeinfo.ValidateDicInEntity(dic, false);
}
public static void RemoveNotExistColumns(this Type typeinfo, List<string> cols)
{
}
/// <summary>
/// 获取所有字段的名称
/// </summary>
/// <param name="typeinfo"></param>
/// <returns></returns>
public static List<string> GetAtrrNames(this Type typeinfo)
{
return typeinfo.GetProperties().Select(c => c.Name).ToList();
}
public static void IsExistColumns(this Type typeinfo)
{
}
public static Dictionary<string, string> GetColumType(this PropertyInfo[] properties)
{
return properties.GetColumType(false);
}
public static Dictionary<string, string> GetColumType(this PropertyInfo[] properties, bool containsKey)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (PropertyInfo property in properties)
{
if (!containsKey && property.IsKey())
{
continue;
}
var keyVal = GetColumnType(property, true);
dictionary.Add(keyVal.Key, keyVal.Value);
}
return dictionary;
}
private static readonly Dictionary<Type, string> entityMapDbColumnType = new Dictionary<Type, string>()
{
{typeof(int), SqlDbTypeName.Int},
{typeof(int?), SqlDbTypeName.Int},
{typeof(long), SqlDbTypeName.BigInt},
{typeof(long?), SqlDbTypeName.BigInt},
{typeof(decimal), "decimal(18, 5)"},
{typeof(decimal?), "decimal(18, 5)"},
{typeof(double), "decimal(18, 5)"},
{typeof(double?), "decimal(18, 5)"},
{typeof(float), "decimal(18, 5)"},
{typeof(float?), "decimal(18, 5)"},
{typeof(Guid), "UniqueIdentifier"},
{typeof(Guid?), "UniqueIdentifier"},
{typeof(byte), "tinyint"},
{typeof(byte?), "tinyint"},
{typeof(string), "nvarchar"}
};
/// <summary>
/// 返回属性的字段及数据库类型
/// </summary>
/// <param name="property"></param>
/// <param name="lenght">是否包括后字段具体长度:nvarchar(100)</param>
/// <returns></returns>
public static KeyValuePair<string, string> GetColumnType(this PropertyInfo property, bool lenght = false)
{
string colType = "";
object objAtrr = property.GetTypeCustomAttributes(typeof(ColumnAttribute), out bool asType);
if (asType)
{
colType = ((ColumnAttribute)objAtrr).TypeName.ToLower();
if (!string.IsNullOrEmpty(colType))
{
//不需要具体长度直接返回
if (!lenght)
{
return new KeyValuePair<string, string>(property.Name, colType);
}
if (colType == "decimal" || colType == "double" || colType == "float")
{
objAtrr = property.GetTypeCustomAttributes(typeof(DisplayFormatAttribute), out asType);
colType += "(" + (asType ? ((DisplayFormatAttribute)objAtrr).DataFormatString : "18,5") + ")";
}
///如果是string,根据 varchar或nvarchar判断最大长度
if (property.PropertyType.ToString() == "System.String")
{
colType = colType.Split("(")[0];
objAtrr = property.GetTypeCustomAttributes(typeof(MaxLengthAttribute), out asType);
if (asType)
{
int length = ((MaxLengthAttribute)objAtrr).Length;
colType += "(" + (length < 1 || length > (colType.StartsWith("n") ? 8000 : 4000)
? "max"
: length.ToString()) + ")";
}
else
{
colType += "(max)";
}
}
return new KeyValuePair<string, string>(property.Name, colType);
}
}
if (entityMapDbColumnType.TryGetValue(property.PropertyType, out string value))
{
colType = value;
}
else
{
colType = SqlDbTypeName.NVarChar;
}
if (lenght && colType == SqlDbTypeName.NVarChar)
{
colType = "nvarchar(max)";
}
return new KeyValuePair<string, string>(property.Name, colType);
}
/// <summary>
///
/// </summary>
/// <param name="array">将数组转换成sql语句</param>
/// <param name="fieldType">指定FieldType数据库字段类型</param>
/// <param name="sql"></param>
/// <returns></returns>
public static string GetArraySql(this object[] array, FieldType fieldType)
{
if (array == null || array.Count() == 0)
{
return string.Empty;
}
string columnType = string.Empty;
List<ArrayEntity> arrrayEntityList = array.Select(x => new ArrayEntity { column1 = x.ToString() }).ToList();
return arrrayEntityList.GetEntitySql(false, null, null, null, fieldType);
}
/// <summary>
///<param name="sql">要执行的sql语句如通过EntityToSqlTempName.Temp_Insert0.ToString()字符串占位生成的的sql语句会把EntityToSqlTempName.Temp_Insert0.ToString()替换成生成的sql临时表数据
/// string sql = " ;DELETE FROM " + typeEntity.Name + " where " + typeEntity.GetKeyName() +
/// " in (select * from " + EntityToSqlTempName.Temp_Insert0.ToString() + ")";
/// </param>
/// </summary>
/// <param name="array"></param>
/// <param name="fieldType">指定生成的数组值的类型</param>
/// <param name="sql"></param>
/// <returns></returns>
public static string GetArraySql(this object[] array, FieldType fieldType, string sql)
{
if (array == null || array.Count() == 0)
{
return string.Empty;
}
string columnType = string.Empty;
List<ArrayEntity> arrrayEntityList = array.Select(x => new ArrayEntity { column1 = x.ToString() }).ToList();
return arrrayEntityList.GetEntitySql(false, sql, null, null, fieldType);
}
public static string GetArraySql<T>(this object[] array, string sql)
{
return array.GetArraySql(typeof(T).GetFieldType(), sql);
}
/// <summary>
/// 根据实体获取key的类型用于update或del操作
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static FieldType GetFieldType(this Type typeEntity)
{
FieldType fieldType;
string columnType = typeEntity.GetProperties().Where(x => x.Name == typeEntity.GetKeyName()).ToList()[0]
.GetColumnType(false).Value;
switch (columnType)
{
case SqlDbTypeName.Int:
fieldType = FieldType.Int;
break;
case SqlDbTypeName.BigInt:
fieldType = FieldType.BigInt;
break;
case SqlDbTypeName.VarChar:
fieldType = FieldType.VarChar;
break;
case SqlDbTypeName.UniqueIdentifier:
fieldType = FieldType.UniqueIdentifier;
break;
default:
fieldType = FieldType.NvarChar;
break;
}
return fieldType;
}
public static string GetEntitySql<T>(this IEnumerable<T> entityList,
bool containsKey = false,
string sql = null,
Expression<Func<T, object>> ignoreFileds = null,
Expression<Func<T, object>> fixedColumns = null,
FieldType? fieldType = null
)
{
if (entityList == null || entityList.Count() == 0) return "";
PropertyInfo[] propertyInfo = typeof(T).GetProperties().ToArray();
if (propertyInfo.Count() == 0)
{
propertyInfo = entityList.ToArray()[0].GetType().GetGenericProperties().ToArray();
}
propertyInfo = propertyInfo.GetGenericProperties().ToArray();
string[] arr = null;
if (fixedColumns != null)
{
arr = fixedColumns.GetExpressionToArray();
PropertyInfo keyProperty = typeof(T).GetKeyProperty();
propertyInfo = propertyInfo
.Where(x => (containsKey && x.Name == keyProperty.Name) || arr.Contains(x.Name)).ToArray();
}
if (ignoreFileds != null)
{
arr = ignoreFileds.GetExpressionToArray();
propertyInfo = propertyInfo.Where(x => !arr.Contains(x.Name)).ToArray();
}
Dictionary<string, string> dictProperties = propertyInfo.GetColumType(containsKey);
if (fieldType != null)
{
string realType = fieldType.ToString();
if ((int)fieldType == 0 || (int)fieldType == 1)
{
realType += "(max)";
}
dictProperties = new Dictionary<string, string>
{{dictProperties.Select(x => x.Key).ToList()[0], realType}};
}
if (dictProperties.Keys.Count * entityList.Count() > 50 * 3000)
{
throw new Exception("写入数据太多,请分开写入。");
}
string cols = string.Join(",", dictProperties.Select(c => "[" + c.Key + "]" + " " + c.Value));
StringBuilder declareTable = new StringBuilder();
string tempTablbe = "#" + EntityToSqlTempName.TempInsert.ToString();
declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")");
declareTable.Append("\r\n");
//参数总数量
int parCount = (dictProperties.Count) * (entityList.Count());
int takeCount = 0;
int maxParsCount = 2050;
if (parCount > maxParsCount)
{
//如果参数总数量超过2100设置每次分批循环写入表的大小
takeCount = maxParsCount / dictProperties.Count;
}
int count = 0;
StringBuilder stringLeft = new StringBuilder();
StringBuilder stringCenter = new StringBuilder();
StringBuilder stringRight = new StringBuilder();
int index = 0;
foreach (T entity in entityList)
{
//每1000行需要分批写入(数据库限制每批至多写入1000行数据)
if (index == 0 || index >= 1000 || takeCount - index == 0)
{
if (stringLeft.Length > 0)
{
declareTable.AppendLine(
stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() +
stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() +
stringRight.Remove(stringRight.Length - 1, 1).ToString());
stringLeft.Clear();
stringCenter.Clear();
stringRight.Clear();
}
stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;");
stringCenter.Append("N'");
index = 0;
count = 0;
}
stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " ");
index++;
foreach (PropertyInfo property in propertyInfo)
{
if (!containsKey && property.IsKey())
{
continue;
}
string par = "@v" + count;
stringLeft.Append(par + ",");
stringCenter.Append(par + " " + dictProperties[property.Name] + ",");
object val = property.GetValue(entity);
if (val == null)
{
stringRight.Append(par + "=NUll,");
}
else
{
stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',");
}
count++;
}
stringLeft.Remove(stringLeft.Length - 1, 1);
stringLeft.Append("),(");
}
if (stringLeft.Length > 0)
{
declareTable.AppendLine(
stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() +
stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() +
stringRight.Remove(stringRight.Length - 1, 1).ToString());
stringLeft.Clear();
stringCenter.Clear();
stringRight.Clear();
}
if (!string.IsNullOrEmpty(sql))
{
sql = sql.Replace(EntityToSqlTempName.TempInsert.ToString(), tempTablbe);
declareTable.AppendLine(sql);
}
else
{
declareTable.AppendLine(" SELECT " +
(string.Join(",", fixedColumns?.GetExpressionToArray() ?? new string[] { "*" })) +
" FROM " + tempTablbe);
}
if (tempTablbe.Substring(0, 1) == "#")
{
declareTable.AppendLine("; drop table " + tempTablbe);
}
return declareTable.ToString();
}
/// <summary>
///此方法适用于数据量少,只有几列数据不超过1W行或几十列数据不超过1000行的情况下使用
/// 大批量的数据考虑其他方式
/// 將datatable生成sql語句替換datatable作為參數傳入存儲過程
/// </summary>
/// <param name="table"></param>
/// <returns></returns>
public static string GetDataTableSql(this DataTable table)
{
Dictionary<string, string> dictCloumn = new Dictionary<string, string>();
for (int i = 0; i < table.Columns.Count; i++)
{
dictCloumn.Add(table.Columns[i].ColumnName, " nvarchar(max)");
}
//参数总数量
int parCount = (dictCloumn.Count) * (table.Rows.Count);
int takeCount = 0;
int maxParsCount = 2050;
if (parCount > maxParsCount)
{
//如果参数总数量超过2100设置每次分批循环写入表的大小
takeCount = maxParsCount / dictCloumn.Count;
}
if (dictCloumn.Keys.Count * table.Rows.Count > 50 * 3000)
{
throw new Exception("写入数据太多,请分开写入。");
}
string cols = string.Join(",", dictCloumn.Select(c => "[" + c.Key + "]" + " " + c.Value));
StringBuilder declareTable = new StringBuilder();
string tempTablbe = "#Temp_Insert0";
declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")");
declareTable.Append("\r\n");
int count = 0;
StringBuilder stringLeft = new StringBuilder();
StringBuilder stringCenter = new StringBuilder();
StringBuilder stringRight = new StringBuilder();
int index = 0;
foreach (DataRow row in table.Rows)
{
//每1000行需要分批写入(数据库限制每批至多写入1000行数据)
if (index == 0 || index >= 1000 || takeCount - index == 0)
{
if (stringLeft.Length > 0)
{
declareTable.AppendLine(
stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() +
stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() +
stringRight.Remove(stringRight.Length - 1, 1).ToString());
stringLeft.Clear();
stringCenter.Clear();
stringRight.Clear();
}
// sbLeft.AppendLine(" INSERT INTO @toInsert0");
stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;");
stringCenter.Append("N'");
index = 0;
count = 0;
}
stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " ");
index++;
foreach (KeyValuePair<string, string> keyValue in dictCloumn)
{
string par = "@v" + count;
stringLeft.Append(par + ",");
stringCenter.Append(par + " " + keyValue.Value + ",");
object val = row[keyValue.Key];
if (val == null)
{
stringRight.Append(par + "=NUll,");
}
else
{
stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',");
}
count++;
}
stringLeft.Remove(stringLeft.Length - 1, 1);
stringLeft.Append("),(");
}
if (stringLeft.Length > 0)
{
declareTable.AppendLine(
stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() +
stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() +
stringRight.Remove(stringRight.Length - 1, 1).ToString());
stringLeft.Clear();
stringCenter.Clear();
stringRight.Clear();
}
declareTable.AppendLine(" SELECT * FROM " + tempTablbe);
if (tempTablbe.Substring(0, 1) == "#")
{
declareTable.AppendLine("; drop table " + tempTablbe);
}
return declareTable.ToString();
}
public static string GetKeyName(this Type typeinfo)
{
return typeinfo.GetProperties().GetKeyName();
}
public static string GetKeyType(this Type typeinfo)
{
string keyType = typeinfo.GetProperties().GetKeyName(true);
if (keyType == "varchar")
{
return "varchar(max)";
}
else if (keyType != "nvarchar")
{
return keyType;
}
else
{
return "nvarchar(max)";
}
}
public static string GetKeyName(this PropertyInfo[] properties)
{
return properties.GetKeyName(false);
}
/// <summary>
/// 获取key列名
/// </summary>
/// <param name="properties"></param>
/// <param name="keyType">true获取key对应类型,false返回对象Key的名称</param>
/// <returns></returns>
public static string GetKeyName(this PropertyInfo[] properties, bool keyType)
{
string keyName = string.Empty;
foreach (PropertyInfo propertyInfo in properties)
{
if (!propertyInfo.IsKey())
continue;
if (!keyType)
return propertyInfo.Name;
var attributes = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), false);
//如果没有ColumnAttribute的需要单独再验证下面只验证有属性的
if (attributes.Length > 0)
return ((ColumnAttribute)attributes[0]).TypeName.ToLower();
else
return GetColumType(new PropertyInfo[] { propertyInfo }, true)[propertyInfo.Name];
}
return keyName;
}
/// <summary>
/// 获取主键字段
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
public static PropertyInfo GetKeyProperty(this Type entity)
{
return entity.GetProperties().GetKeyProperty();
}
public static PropertyInfo GetKeyProperty(this PropertyInfo[] properties)
{
return properties.Where(c => c.IsKey()).FirstOrDefault();
}
public static bool IsKey(this PropertyInfo propertyInfo)
{
object[] keyAttributes = propertyInfo.GetCustomAttributes(typeof(KeyAttribute), false);
if (keyAttributes.Length > 0)
return true;
return false;
}
private static string[] _userEditFields { get; set; }
/// <summary>
/// 判断是否包含某个属性:
/// 如 [Editable(true)]
// public string MO { get; set; }包含Editable
/// </summary>
/// <param name="propertyInfo"></param>
/// <param name="type"></param>
/// <returns></returns>
public static bool ContainsCustomAttributes(this PropertyInfo propertyInfo, Type type)
{
propertyInfo.GetTypeCustomAttributes(type, out bool contains);
return contains;
}
public static List<PropertyInfo> ContainsCustomAttributes(this Type obj, Type containType)
{
List<PropertyInfo> proList = new List<PropertyInfo>();
foreach (PropertyInfo pro in obj.GetProperties())
{
if (pro.GetTypeCustomAttributes(containType) != null)
{
proList.Add(pro);
}
}
return proList;
}
/// <summary>
/// 获取PropertyInfo指定属性
/// </summary>
/// <param name="propertyInfo"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object GetTypeCustomAttributes(this PropertyInfo propertyInfo, Type type, out bool asType)
{
object[] attributes = propertyInfo.GetCustomAttributes(type, false);
if (attributes.Length == 0)
{
asType = false;
return new string[0];
}
asType = true;
return attributes[0];
}
/// <summary>
/// 验证集合的属性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entityList"></param>
/// <param name="expression"></param>
/// <returns></returns>
public static void ValidationEntityList<T>(this List<T> entityList,
Expression<Func<T, object>> expression = null)
{
WebResponseContent responseData = new WebResponseContent();
foreach (T entity in entityList)
{
entity.ValidationEntity(expression);
}
}
/// <summary>
/// 指定需要验证的字段
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <param name="expression">对指定属性进行验证x=>{x.Name,x.Size}</param>
/// <returns></returns>
public static void ValidationEntity<T>(this T entity, Expression<Func<T, object>> expression = null,
Expression<Func<T, object>> validateProperties = null)
{
ValidationEntity<T>(entity, expression?.GetExpressionProperty<T>(),
validateProperties?.GetExpressionProperty<T>());
}
/// <summary>
/// specificProperties=null并且validateProperties=null对所有属性验证只验证其是否合法不验证是否为空(除属性标识指定了不能为空外)
/// specificProperties!=null对指定属性校验并且都必须有值
/// null并且validateProperties!=null对指定属性校验不判断是否有值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <param name="specificProperties">验证指定的属性,并且非空判断</param>
/// <param name="validateProperties">验证指定属性,只对字段合法性判断,不验证是否为空</param>
/// <returns></returns>
public static void ValidationEntity<T>(this T entity, string[] specificProperties,
string[] validateProperties = null)
{
if (entity == null)
{
throw new Exception("对象不能为null");
}
PropertyInfo[] propertyArray = typeof(T).GetProperties();
//若T为object取不到属性
if (propertyArray.Length == 0)
{
propertyArray = entity.GetType().GetProperties();
}
List<PropertyInfo> compareProper = new List<PropertyInfo>();
//只验证数据合法性,验证非空
if (specificProperties != null && specificProperties.Length > 0)
{
compareProper.AddRange(propertyArray.Where(x => specificProperties.Contains(x.Name)));
}
//只验证数据合法性,不验证非空
if (validateProperties != null && validateProperties.Length > 0)
{
compareProper.AddRange(propertyArray.Where(x => validateProperties.Contains(x.Name)));
}
if (compareProper.Count() > 0)
{
propertyArray = compareProper.ToArray();
}
foreach (PropertyInfo propertyInfo in propertyArray)
{
object value = propertyInfo.GetValue(entity);
//设置默认状态的值
if (propertyInfo.Name == "Enable")
{
if (value == null)
{
propertyInfo.SetValue(entity, 0);
continue;
}
}
//若存在specificProperties并且属性为数组specificProperties中的值校验时就需要判断是否为空
var reslut = propertyInfo.ValidationProperty(value,
specificProperties != null && specificProperties.Contains(propertyInfo.Name) ? true : false
);
if (!reslut.Item1)
{
throw new Exception(reslut.Item2);
}
}
}
/// <summary>
/// 获取数据库类型不带长度如varchar(100),只返回的varchar
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
public static string GetSqlDbType(this PropertyInfo propertyInfo)
{
string dbType = propertyInfo.GetTypeCustomValue<ColumnAttribute>(x => new { x.TypeName });
if (string.IsNullOrEmpty(dbType))
{
return dbType;
}
dbType = dbType.ToLower();
if (dbType.Contains(SqlDbTypeName.NVarChar))
{
dbType = SqlDbTypeName.NVarChar;
}
else if (dbType.Contains(SqlDbTypeName.VarChar))
{
dbType = SqlDbTypeName.VarChar;
}
else if (dbType.Contains(SqlDbTypeName.NChar))
{
dbType = SqlDbTypeName.NChar;
}
else if (dbType.Contains(SqlDbTypeName.Char))
{
dbType = SqlDbTypeName.Char;
}
return dbType;
}
/// <summary>
/// 验证数据库字段类型与值是否正确,
/// </summary>
/// <param name="propertyInfo">propertyInfo为当字段当前字段必须有ColumnAttribute属性,
/// 如字段:标识为数据库int类型[Column(TypeName="int")] public int Id { get; set; }
/// 如果是小数float或Decimal必须对propertyInfo字段加DisplayFormatAttribute属性
/// </param>
/// <param name="value"></param>
/// <returns>IEnumerable<(bool, string, object)> bool成否校验成功,string校验失败信息,object,当前校验的值</returns>
public static IEnumerable<(bool, string, object)> ValidationValueForDbType(this PropertyInfo propertyInfo,
params object[] values)
{
string dbTypeName = propertyInfo.GetTypeCustomValue<ColumnAttribute>(c => c.TypeName);
foreach (object value in values)
{
yield return dbTypeName.ValidationVal(value, propertyInfo);
}
}
public static bool ValidationRquiredValueForDbType(this PropertyInfo propertyInfo, object value,
out string message)
{
if (value == null || value?.ToString()?.Trim() == "")
{
message = $"{propertyInfo.GetDisplayName()}不能为空";
return false;
}
var result = propertyInfo.GetProperWithDbType().ValidationVal(value, propertyInfo);
message = result.Item2;
return result.Item1;
}
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}
};
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="dbType">数据库字段类型(如varchar,nvarchar,decimal,不要带后面长度如:varchar(50))</param>
/// <param name="value">值</param>
/// <param name="propertyInfo">要验证的类的属性若不为null则会判断字符串的长度是否正确</param>
/// <returns>(bool, string, object)bool成否校验成功,string校验失败信息,object,当前校验的值</returns>
public static (bool, string, object) ValidationVal(this string dbType, object value,
PropertyInfo propertyInfo = null)
{
if (string.IsNullOrEmpty(dbType))
{
dbType = propertyInfo != null ? propertyInfo.GetProperWithDbType() : SqlDbTypeName.NVarChar;
}
dbType = dbType.ToLower();
string val = value?.ToString();
//验证长度
string reslutMsg = string.Empty;
if (dbType == SqlDbTypeName.Int || dbType == SqlDbTypeName.BigInt)
{
if (!StringExtension.IsInt(value))
reslutMsg = "只能为有效整数";
}
else if (dbType == SqlDbTypeName.DateTime
|| dbType == SqlDbTypeName.Date
|| dbType == SqlDbTypeName.SmallDateTime
|| dbType == SqlDbTypeName.SmallDate
)
{
if (!StringExtension.IsDate(value))
reslutMsg = "必须为日期格式";
}
else if (dbType == SqlDbTypeName.Float || dbType == SqlDbTypeName.Decimal || dbType == SqlDbTypeName.Double)
{
string formatString = string.Empty;
if (propertyInfo != null)
formatString = propertyInfo.GetTypeCustomValue<DisplayFormatAttribute>(x => x.DataFormatString);
//if (string.IsNullOrEmpty(formatString))
// throw new Exception("请对字段" + propertyInfo?.Name + "添加DisplayFormat属性标识");
if (!StringExtension.IsNumber(val, formatString))
{
string[] arr = (formatString ?? "10,0").Split(',');
reslutMsg = $"整数{arr[0]}最多位,小数最多{arr[1]}位";
}
}
else if (dbType == SqlDbTypeName.UniqueIdentifier)
{
if (!StringExtension.IsGuid(val))
{
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 > 20000)
{
reslutMsg = $"字符长度最多【20000】";
}
else
{
int length =
StringExtension.GetInt(
propertyInfo.GetTypeCustomValue<MaxLengthAttribute>(x => new { x.Length }));
if (length == 0)
{
return (true, null, null);
}
//判断双字节与单字段
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 = propertyInfo.GetDisplayName() + reslutMsg;
}
return (reslutMsg == "" ? true : false, reslutMsg, value);
}
public static string GetDisplayName(this PropertyInfo property)
{
string displayName = property.GetTypeCustomValue<DisplayAttribute>(x => new { x.Name });
if (string.IsNullOrEmpty(displayName))
{
return property.Name;
}
return displayName;
}
/// <summary>
/// 验证每个属性的值是否正确
/// </summary>
/// <param name="propertyInfo"></param>
/// <param name="objectVal">属性的值</param>
/// <param name="required">是否指定当前属性必须有值</param>
/// <returns></returns>
public static (bool, string, object) ValidationProperty(this PropertyInfo propertyInfo, object objectVal,
bool required)
{
if (propertyInfo.IsKey())
{
return (true, null, objectVal);
}
string val = objectVal == null ? "" : objectVal.ToString().Trim();
string requiredMsg = string.Empty;
if (required)
{
var reuireVal =
propertyInfo.GetTypeCustomValues<RequiredAttribute>(x => new { x.AllowEmptyStrings, x.ErrorMessage });
if (reuireVal != null && !Convert.ToBoolean(reuireVal["AllowEmptyStrings"]))
{
required = true;
requiredMsg = reuireVal["ErrorMessage"];
}
}
//如果不要求为必填项并且值为空,直接返回
if (!required && string.IsNullOrEmpty(val))
return (true, null, objectVal);
if ((required && val == string.Empty))
{
if (requiredMsg != "") return (false, requiredMsg, objectVal);
string propertyName = propertyInfo.GetTypeCustomValue<DisplayAttribute>(x => new { x.Name });
return (false,
requiredMsg + (string.IsNullOrEmpty(propertyName) ? propertyInfo.Name : propertyName) + "不能为空",
objectVal);
}
//列名
string typeName = propertyInfo.GetSqlDbType();
//如果没有ColumnAttribute的需要单独再验证下面只验证有属性的
if (typeName == null)
{
return (true, null, objectVal);
}
//验证长度
return typeName.ValidationVal(val, propertyInfo);
}
/// <summary>
/// 获取属性的指定属性
/// </summary>
/// <param name="propertyInfo"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object GetTypeCustomAttributes(this MemberInfo member, Type type)
{
object[] obj = member.GetCustomAttributes(type, false);
if (obj.Length == 0) return null;
return obj[0];
}
/// <summary>
/// 获取类的指定属性
/// </summary>
/// <param name="propertyInfo"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object GetTypeCustomAttributes(this Type entity, Type type)
{
object[] obj = entity.GetCustomAttributes(type, false);
if (obj.Length == 0) return null;
return obj[0];
}
/// <summary>
/// 获取类的多个指定属性的值
/// </summary>
/// <param name="member">当前类</param>
/// <param name="type">指定的类</param>
/// <param name="expression">指定属性的值 格式 Expression<Func<entityt, object>> exp = x => new { x.字段1, x.字段2 };</param>
/// <returns>返回的是字段+value</returns>
public static Dictionary<string, string> GetTypeCustomValues<TEntity>(this MemberInfo member,
Expression<Func<TEntity, object>> expression)
{
var attr = member.GetTypeCustomAttributes(typeof(TEntity));
if (attr == null)
{
return null;
}
string[] propertyName = expression.GetExpressionProperty();
Dictionary<string, string> propertyKeyValues = new Dictionary<string, string>();
foreach (PropertyInfo property in attr.GetType().GetProperties())
{
if (propertyName.Contains(property.Name))
{
propertyKeyValues[property.Name] = (property.GetValue(attr) ?? string.Empty).ToString();
}
}
return propertyKeyValues;
}
/// <summary>
/// 获取类的单个指定属性的值(只会返回第一个属性的值)
/// </summary>
/// <param name="member">当前类</param>
/// <param name="type">指定的类</param>
/// <param name="expression">指定属性的值 格式 Expression<Func<entityt, object>> exp = x => new { x.字段1, x.字段2 };</param>
/// <returns></returns>
public static string GetTypeCustomValue<TEntity>(this MemberInfo member,
Expression<Func<TEntity, object>> expression)
{
var propertyKeyValues = member.GetTypeCustomValues(expression);
if (propertyKeyValues == null || propertyKeyValues.Count == 0)
{
return null;
}
return propertyKeyValues.First().Value ?? "";
}
/// <summary>
/// 判断hash的列是否为对应的实体并且值是否有效
/// </summary>
/// <param name="typeinfo"></param>
/// <param name="dic"></param>
/// <param name="removeNotContains">移除不存在字段</param>
/// <returns></returns>
public static string ValidateDicInEntity(this Type typeinfo, Dictionary<string, object> dic,
bool removeNotContains, string[] ignoreFields = null)
{
return typeinfo.ValidateDicInEntity(dic, removeNotContains, true, ignoreFields);
}
public static string ValidateDicInEntity(this Type type, List<Dictionary<string, object>> dicList,
bool removeNotContains, bool removerKey, string[] ignoreFields = null)
{
PropertyInfo[] propertyInfo = type.GetProperties();
string reslutMsg = string.Empty;
foreach (Dictionary<string, object> dic in dicList)
{
reslutMsg = type.ValidateDicInEntity(dic, propertyInfo, removeNotContains, removerKey, ignoreFields);
if (!string.IsNullOrEmpty(reslutMsg))
return reslutMsg;
}
return reslutMsg;
}
public static string ValidateDicInEntity(this Type type, Dictionary<string, object> dic, bool removeNotContains,
bool removerKey, string[] ignoreFields = null)
{
return type.ValidateDicInEntity(dic, null, removeNotContains, removerKey, ignoreFields);
}
/// <summary>
/// 判断hash的列是否为对应的实体并且值是否有效
/// </summary>
/// <param name="typeinfo"></param>
/// <param name="dic"></param>
/// <param name="removeNotContains">移除不存在字段</param>
/// <param name="removerKey">移除主键</param>
/// <returns></returns>
private static string ValidateDicInEntity(this Type typeinfo, Dictionary<string, object> dic,
PropertyInfo[] propertyInfo, bool removeNotContains, bool removerKey, string[] ignoreFields = null)
{
if (dic == null || dic.Count == 0)
{
return "参数无效";
}
if (propertyInfo == null)
propertyInfo = typeinfo.GetProperties().Where(x => x.PropertyType.Name != "List`1").ToArray();
// 不存在的字段直接移除
dic.Where(x => !propertyInfo.Any(p => p.Name == x.Key)).Select(s => s.Key).ToList().ForEach(f =>
{
dic.Remove(f);
});
string keyName = typeinfo.GetKeyName();
//移除主键
if (removerKey)
{
dic.Remove(keyName);
}
foreach (PropertyInfo property in propertyInfo)
{
//忽略与主键的字段不做验证
if (property.Name == keyName || (ignoreFields != null && ignoreFields.Contains(property.Name)))
continue;
//不在编辑中的列,是否也要必填
if (!dic.ContainsKey(property.Name))
{
//移除主键默认为新增数据,将不在编辑列中的有默认值的数据设置为默认值
//如果为true默认为添加功能添加操作所有不能为空的列也必须要提交
if (property.GetCustomAttributes(typeof(RequiredAttribute)).Count() > 0
&& property.PropertyType != typeof(int)
&& property.PropertyType != typeof(long)
&& property.PropertyType != typeof(byte)
&& property.PropertyType != typeof(decimal)
)
{
return property.GetTypeCustomValue<DisplayAttribute>(x => x.Name) + "为必须提交项";
}
continue;
}
bool isEdit = property.ContainsCustomAttributes(typeof(EditableAttribute));
//不是编辑列的直接移除,并且不是主键
//removerKey=true不保留主键直接移除
//removerKey=false,保留主键,属性与主键不同的直接移除
// if (!isEdit && (removerKey || (!removerKey && property.Name != keyName)))
if (!isEdit)
{
if (property.GetCustomAttributes(typeof(RequiredAttribute)).Count() > 0)
{
return property.GetTypeCustomValue<DisplayAttribute>(x => x.Name) + "没有配置好Model为编辑列";
}
dic.Remove(property.Name);
continue;
}
////移除忽略的不保存的数据
//if (property.ContainsCustomAttributes(typeof(JsonIgnoreAttribute)))
//{
// hash.Remove(property.Name);
// continue;
//}
//验证数据类型,不验证是否为空
var result = property.ValidationProperty(dic[property.Name], false);
if (!result.Item1)
return result.Item2;
//将所有空值设置为null
if (dic[property.Name] != null && dic[property.Name].ToString() == string.Empty)
dic[property.Name] = null;
}
return string.Empty;
}
private static object MapToInstance(this Type reslutType, object sourceEntity, PropertyInfo[] sourcePro,
PropertyInfo[] reslutPro, string[] sourceFilterField, string[] reslutFilterField, string mapType = null)
{
mapType = mapType ?? GetMapType(reslutType);
if (sourcePro == null)
{
sourcePro = sourceEntity.GetType().GetProperties();
}
if (reslutPro == null)
{
reslutPro = reslutType.GetProperties();
;
}
object newObj = Activator.CreateInstance(reslutType);
if (mapType == "Dictionary")
{
if (sourceFilterField != null && sourceFilterField.Length > 0)
{
sourcePro = sourcePro.Where(x => sourceFilterField.Contains(x.Name)).ToArray();
}
foreach (var property in sourcePro)
{
(newObj as System.Collections.IDictionary).Add(property.Name, property.GetValue(sourceEntity));
}
return newObj;
}
if (reslutFilterField != null && reslutFilterField.Count() > 0)
{
reslutPro.Where(x => reslutFilterField.Contains(x.Name));
}
foreach (var property in reslutPro)
{
PropertyInfo info = sourcePro.Where(x => x.Name == property.Name).FirstOrDefault();
if (!(info != null && info.PropertyType == property.PropertyType))
continue;
property.SetValue(newObj, info.GetValue(sourceEntity));
}
return newObj;
}
private static string GetMapType(Type type)
{
return typeof(Dictionary<,>) == type ? "Dictionary" : "entity";
}
/// <summary>
/// 将数据源映射到新的数据中,目前只支持List<TSource>映射到List<TResult>或TSource映射到TResult
/// 目前只支持Dictionary或实体类型
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="source"></param>
/// <param name="resultExpression">只映射返回对象的指定字段,若为null则默认为全部字段</param>
/// <param name="sourceExpression">只映射数据源对象的指定字段,若为null则默认为全部字段</param>
/// <returns></returns>
public static TResult MapToObject<TSource, TResult>(this TSource source,
Expression<Func<TResult, object>> resultExpression,
Expression<Func<TSource, object>> sourceExpression = null
) where TResult : class
{
if (source == null)
return null;
string[] sourceFilterField = sourceExpression == null
? typeof(TSource).GetProperties().Select(x => x.Name).ToArray()
: sourceExpression.GetExpressionProperty();
string[] reslutFilterField = resultExpression?.GetExpressionProperty();
if (!(source is System.Collections.IList))
return MapToInstance(typeof(TResult), source, null, null, sourceFilterField, reslutFilterField) as
TResult;
Type sourceType = null;
Type resultType = null;
System.Collections.IList sourceList = source as System.Collections.IList;
sourceType = sourceList[0].GetType();
resultType = (typeof(TResult)).GenericTypeArguments[0];
System.Collections.IList reslutList = Activator.CreateInstance(typeof(TResult)) as System.Collections.IList;
PropertyInfo[] sourcePro = sourceType.GetProperties();
PropertyInfo[] resultPro = resultType.GetProperties();
string mapType = GetMapType(resultType);
for (int i = 0; i < sourceList.Count; i++)
{
var reslutobj = MapToInstance(resultType, sourceList[i], sourcePro, resultPro, sourceFilterField,
reslutFilterField, mapType);
reslutList.Add(reslutobj);
}
return reslutList as TResult;
}
/// <summary>
/// 将一个实体的赋到另一个实体上,应用场景:
/// 两个实体a a1= new a();b b1= new b(); a1.P=b1.P; a1.Name=b1.Name;
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="source"></param>
/// <param name="result"></param>
/// <param name="expression">指定对需要的字段赋值,格式x=>new {x.Name,x.P},返回的结果只会对Name与P赋值</param>
public static void MapValueToEntity<TSource, TResult>(this TSource source, TResult result,
Expression<Func<TResult, object>> expression = null) where TResult : class
{
if (source == null)
return;
string[] fields = expression?.GetExpressionToArray();
PropertyInfo[] reslutPro = fields == null
? result.GetType().GetProperties()
: result.GetType().GetProperties().Where(x => fields.Contains(x.Name)).ToArray();
PropertyInfo[] sourcePro = source.GetType().GetProperties();
foreach (var property in reslutPro)
{
PropertyInfo info = sourcePro.Where(x => x.Name == property.Name).FirstOrDefault();
if (info != null && info.PropertyType == property.PropertyType)
{
property.SetValue(result, info.GetValue(source));
}
}
}
}
public class ArrayEntity
{
public string column1 { get; set; }
}
public enum FieldType
{
VarChar = 0,
NvarChar,
Int,
BigInt,
UniqueIdentifier
}
public enum EntityToSqlTempName
{
TempInsert = 0
}
}