using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Collections.Concurrent;
using System.Reflection.Emit;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Linq.Expressions;
using System.Data.SqlClient;
using System.Configuration;
using XYY.Core.Standard.Data.Infrastructure;
using XYY.Common.Standard;
using XYY.Common.Standard.Attributes;
using KeyAttribute = System.ComponentModel.DataAnnotations.KeyAttribute;
using XYY.Core;
using System.Threading.Tasks;
namespace Dapper
{
public static class SqlMapperExtensions
{
public enum Operate
{
//等于
_Eq_,
///
/// 日期等于
///
_DataEq_,
//不等于
_NotEq_,
//任意匹配
_Co_,
//小于
_Le_,
//大于
_Gr_,
//小于等于
_LeE_,
//大于等于
_GrE_,
//IN
_In_,
//NOT IN
_NotIn_,
_IsNlNumGr_,
_IsNlNumLe_,
_IsNotNl_,
_IsNl_
}
public interface IProxy
{
bool IsDirty { get; set; }
}
private static readonly ConcurrentDictionary> KeyProperties = new ConcurrentDictionary>();
private static readonly ConcurrentDictionary> TypeProperties = new ConcurrentDictionary>();
private static readonly ConcurrentDictionary GetQueries = new ConcurrentDictionary();
private static readonly ConcurrentDictionary TypeTableName = new ConcurrentDictionary();
private static readonly ConcurrentDictionary> ComputedProperties = new ConcurrentDictionary>();
private static IEnumerable KeyPropertiesCache(Type type)
{
if (KeyProperties.ContainsKey(type.TypeHandle))
{
return KeyProperties[type.TypeHandle];
}
var allProperties = TypePropertiesCache(type);
var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();
if (keyProperties.Count == 0)
{
var idProp = allProperties.Where(p => p.Name.ToLower() == "id").FirstOrDefault();
if (idProp != null)
{
keyProperties.Add(idProp);
}
}
KeyProperties[type.TypeHandle] = keyProperties;
return keyProperties;
}
private static IEnumerable ComputedPropertiesCache(Type type)
{
if (ComputedProperties.ContainsKey(type.TypeHandle))
{
return ComputedProperties[type.TypeHandle];
}
var allProperties = TypePropertiesCache(type);
var computedProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a is NoDbAttribute)).ToList();
ComputedProperties[type.TypeHandle] = computedProperties;
return computedProperties;
}
private static IEnumerable TypePropertiesCache(Type type)
{
if (TypeProperties.ContainsKey(type.TypeHandle))
{
return TypeProperties[type.TypeHandle];
}
var properties = type.GetProperties();
TypeProperties[type.TypeHandle] = properties;
return properties;
}
///
/// Returns a single entity by a single id from table "Ts". T must be of interface type.
/// Id must be marked with [Key] attribute.
/// Created entity is tracked/intercepted for changes and used by the Update() extension.
///
/// Interface type to create and populate
/// Open SqlConnection
/// Id of the entity to get, must be marked with [Key] attribute
/// Entity of T
public static async Task GetAsync(this IUnitOfWork unitOfWork, dynamic id, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
//if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
//{
var keys = KeyPropertiesCache(type);
if (keys.Count() > 1)
throw new DataException("Get only supports an entity with a single [Key] property");
if (keys.Count() == 0)
throw new DataException("Get only supports en entity with a [Key] property");
var onlyKey = keys.First();
var name = GetTableName(type);
// TODO: pluralizer
// TODO: query information schema and only select fields that are both in information schema and underlying class / interface
sql = "select * from " + name + "(nolock) where " + onlyKey.Name + " = @id";
// GetQueries[type.TypeHandle] = sql;
//}
#if DEBUG
Trace.WriteLine(sql);
#endif
var dynParms = new DynamicParameters();
dynParms.Add("@id", id);
T obj = null;
if (type.IsInterface)
{
var r = await unitOfWork.Connection.QueryAsync(sql, dynParms);
var res = r.FirstOrDefault() as IDictionary;
if (res == null)
return (T)((object)null);
obj = ProxyGenerator.GetInterfaceProxy();
foreach (var property in TypePropertiesCache(type))
{
var val = res[property.Name];
property.SetValue(obj, val, null);
}
((IProxy)obj).IsDirty = false; //reset change tracking and return
}
else
{
var r = await unitOfWork.Connection.QueryAsync(sql, dynParms, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
obj = r.FirstOrDefault();
}
return obj;
}
private static string TranslateWhere(List paramer)
{
string where = "";
foreach (var item in paramer)
{
Operate operate = (Operate)Enum.Parse(typeof(Operate), string.Format("{0}{1}{0}", "_", item.Method));
switch (operate)
{
case Operate._Eq_:
where += string.Format(" AND {0} = N'{1}' ", item.Filed.Replace("$$", "."), item.Value);
break;
case Operate._DataEq_:
where += string.Format(" AND CONVERT(VARCHAR(8),{0},112) = '{1}' ", item.Filed, item.Value.Replace("-", ""));
break;
case Operate._NotEq_:
where += string.Format(" AND {0} != N'{1}' ", item.Filed, item.Value);
break;
case Operate._Co_:
where += string.Format(" AND {0} like '%{1}%' ", item.Filed, item.Value);
break;
case Operate._Le_:
if (item.Value.IsMatch("^(-?[0-9]*[.]*[0-9]{0,3})$"))
{
where += string.Format(" AND {0} < {1} ", item.Filed, item.Value);
}
else
{
where += string.Format(" AND {0} < '{1}' ", item.Filed, item.Value);
}
break;
case Operate._Gr_:
if (item.Value.IsMatch("^(-?[0-9]*[.]*[0-9]{0,3})$"))
{
where += string.Format(" AND {0} > {1} ", item.Filed, item.Value);
}
else
{
where += string.Format(" AND {0} > '{1}' ", item.Filed, item.Value);
}
break;
case Operate._LeE_:
if (item.Value.IsMatch("^(-?[0-9]*[.]*[0-9]{0,3})$"))
{
where += string.Format(" AND {0} <= {1} ", item.Filed, item.Value);
}
if (item.Value.IsMatch("\\d{4}-\\d{2}-\\d{2}"))
{
where += string.Format(" AND {0} <= '{1} 23:59:59.999'", item.Filed, item.Value);
}
else
{
where += string.Format(" AND {0} <= '{1}' ", item.Filed, item.Value);
}
break;
case Operate._GrE_:
if (item.Value.IsMatch("^(-?[0-9]*[.]*[0-9]{0,3})$"))
{
where += string.Format(" AND {0} >= {1} ", item.Filed, item.Value);
}
if (item.Value.IsMatch("\\d{4}-\\d{2}-\\d{2}"))
{
where += string.Format(" AND {0} >= '{1} 00:00:00.000'", item.Filed, item.Value);
}
else
{
where += string.Format(" AND {0} >= '{1}' ", item.Filed, item.Value);
}
break;
case Operate._In_:
if (item.Value.IsMatch("^\\d$"))
{
where += string.Format(" AND {0} in ( {1} )", item.Filed, item.Value);
}
else
{
if (item.Value.IndexOf(',') > -1)
{
string value = item.Value.Trim(',');
if (value != string.Empty)
{
where += string.Format(" AND {0} in ( '{1}' )", item.Filed, value.Replace(",", "','"));
}
}
else if (item.Value.IndexOf('\n') > -1)
{
string value = item.Value.Trim('\n');
if (value != string.Empty)
{
where += string.Format(" AND {0} in ( '{1}' )", item.Filed, value.Replace("\n", "','"));
}
}
else
{
if (item.Value != string.Empty)
{
where += string.Format(" AND {0} in ( '{1}' )", item.Filed, item.Value);
}
}
}
break;
case Operate._NotIn_:
where += string.Format(" AND {0} not in ( {1} )", item.Filed, item.Value);
break;
case Operate._IsNlNumGr_:
where += string.Format(" AND isnull({0},0) >= {1} ", item.Filed, item.Value);
break;
case Operate._IsNlNumLe_:
where += string.Format(" AND isnull({0},0) <= {1} ", item.Filed, item.Value);
break;
case Operate._IsNotNl_:
where += string.Format(" AND isnull({0},'') != '{1}' ", item.Filed, item.Value);
break;
case Operate._IsNl_:
where += string.Format(" AND isnull({0},'') = '{1}' ", item.Filed, item.Value);
break;
}
}
return where;
}
public static string GetWhere(this IUnitOfWork unitOfWork, List paramer)
{
return TranslateWhere(paramer);
}
///
/// 查询分页数据(优化)
/// 该方法必须包含主键
///
///
///
///
///
///
public static async Task> GetPagingList2Async(this IUnitOfWork unitOfWork, QueryModel queryModel, int? commandTimeount = null) where T : class
{
var type = typeof(T);
string sql;
string tableName = GetTableName(type);
sql = @"select * from ( select Row_Number()over(order by {0})rowId,{4} from " + tableName + @"(nolock) {1})T
join " + tableName + @" b on t.{4} = b.{4} where rowId between {2} and {3};
select count(0) from " + tableName + @"(nolock) {1}";
if (string.IsNullOrEmpty(queryModel.Key))
queryModel.Key = "Id";
//sql = @"select * from ( select Row_Number()over(order by {0})rowId, * from " + tableName + @"(nolock) {1} )T where rowId between {2} and {3};
// select count(0) from " + tableName + @"(nolock) {1}";
string whereString = "WHERE 1=1 ";
string sortString = "";
//Where
if (queryModel.QueryParamer != null && queryModel.QueryParamer.Count > 0)
{
var where = TranslateWhere(queryModel.QueryParamer);
if (!string.IsNullOrWhiteSpace(where))
{
whereString += where;
}
}
if (queryModel.DataPermission != null && queryModel.DataPermission.Count > 0)
{//存在数据权限
var allProperties = TypePropertiesCache(type);
for (var i = 0; i < allProperties.Count(); i++)
{
var property = allProperties.ElementAt(i);
var identity = property.GetCustomAttribute();
if (identity == null)
continue;
var permission = queryModel.DataPermission.Find(x => x.PermissioinType == identity.PermissionType.GetValue());
if (permission == null)
continue;
if (string.IsNullOrWhiteSpace(permission.Value))
continue;
whereString += " AND {0} IN ({1})".Formater(property.Name, permission.Value.TrimEnd(','));
}
}
//if (expression != null)
//{
// var translator = new QueryTranslator();
// whereString = " where " + translator.Translate(expression);
//}
//Sort
if (queryModel.QuerySort == null || queryModel.QuerySort.Count == 0)
{
var keys = KeyPropertiesCache(type);
if (keys.Count() == 0)
throw new DataException("查询对象中必须包含主键");
sortString = keys.First().Name;
}
else
{
foreach (var item in queryModel.QuerySort)
{
sortString += " " + item.Field + " " + (item.IsDesc ? "DESC" : "") + ",";
}
sortString = sortString.TrimEnd(',');
}
sql = string.Format(sql, sortString, whereString, (queryModel.PageIndex - 1) * queryModel.PageSize + 1, queryModel.PageSize * queryModel.PageIndex, queryModel.Key);
var grid = await unitOfWork.Connection.QueryMultipleAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeount);
#if DEBUG
Trace.WriteLine(sql);
#endif
return new PageResult()
{
Result = grid.Read(),
TotalCount = grid.Read().SingleOrDefault(),
PageIndex = queryModel.PageIndex,
PageSize = queryModel.PageIndex
};
}
public static async Task> GetPagingListAsync(this IUnitOfWork unitOfWork, QueryModel queryModel, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
string tableName = GetTableName(type);
sql = @"select * from ( select Row_Number()over(order by {0})rowId, * from " + tableName + @"(nolock) {1} )T where rowId between {2} and {3};
select count(0) from " + tableName + @"(nolock) {1}";
string whereString = "WHERE 1=1 ";
string sortString = "";
//Where
if (queryModel != null && queryModel.QueryParamer != null && queryModel.QueryParamer.Count > 0)
{
var where = TranslateWhere(queryModel.QueryParamer);
if (!string.IsNullOrWhiteSpace(where))
{
whereString += where;
}
}
if (queryModel.DataPermission != null && queryModel.DataPermission.Count > 0)
{//存在数据权限
var allProperties = TypePropertiesCache(type);
for (var i = 0; i < allProperties.Count(); i++)
{
var property = allProperties.ElementAt(i);
var identity = property.GetCustomAttribute();
if (identity == null)
continue;
var permission = queryModel.DataPermission.Find(x => x.PermissioinType == identity.PermissionType.GetValue());
if (permission == null)
continue;
if (string.IsNullOrWhiteSpace(permission.Value))
continue;
whereString += " AND {0} IN ({1})".Formater(property.Name, permission.Value.TrimEnd(','));
}
}
//if (expression != null)
//{
// var translator = new QueryTranslator();
// whereString = " where " + translator.Translate(expression);
//}
//Sort
if (queryModel.QuerySort == null || queryModel.QuerySort.Count == 0)
{
var keys = KeyPropertiesCache(type);
if (keys.Count() == 0)
throw new DataException("查询对象中必须包含主键");
sortString = keys.First().Name;
}
else
{
foreach (var item in queryModel.QuerySort)
{
sortString += " " + item.Field + " " + (item.IsDesc ? "DESC" : "") + ",";
}
sortString = sortString.TrimEnd(',');
}
sql = string.Format(sql, sortString, whereString, (queryModel.PageIndex - 1) * queryModel.PageSize + 1, queryModel.PageSize * queryModel.PageIndex);
var grid = await unitOfWork.Connection.QueryMultipleAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
#if DEBUG
Trace.WriteLine(sql);
#endif
return new PageResult()
{
Result = grid.Read(),
TotalCount = grid.Read().SingleOrDefault(),
PageIndex = queryModel.PageIndex,
PageSize = queryModel.PageIndex
};
}
public static async Task> QueryBySqlAsync(this IUnitOfWork unitOfWork, string sql, int? commandTimeout = null, object param = null) where T : class
{
#if DEBUG
Trace.WriteLine(sql);
#endif
return await unitOfWork.Connection.QueryAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout, param: param);
}
public static async Task ExecuteScalarAsync(this IUnitOfWork unitOfWork, string sql, int? commandTimeout = null) where T : class
{
#if DEBUG
Trace.WriteLine(sql);
#endif
return await unitOfWork.Connection.ExecuteScalarAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
}
public static async Task ExecuteAsync(this IUnitOfWork unitOfWork, string sql)
{
await ExecuteAsync(unitOfWork, sql, null);
}
public static async Task ExecuteAsync(this IUnitOfWork unitOfWork, string sql, object obj)
{
await unitOfWork.Connection.ExecuteAsync(sql, obj, unitOfWork.Transaction);
}
public static async Task> GetPagingListAsync(this IUnitOfWork unitOfWork, QueryModel queryModel, string customSql, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
string tableName = GetTableName(type);
sql = @"select * from ( select Row_Number()over(order by {0})rowId, * from ( " + customSql + " ) " + tableName + @" {1} )T where rowId between {2} and {3};
select count(0) from ( " + customSql + " ) " + tableName + @" {1}";
string whereString = " WHERE 1=1 ";
string sortString = "";
//Where
if (queryModel.QueryParamer != null && queryModel.QueryParamer.Count > 0)
{
var where = TranslateWhere(queryModel.QueryParamer);
if (!string.IsNullOrWhiteSpace(where))
{
whereString += where;
}
}
//if (expression != null)
//{
// var translator = new QueryTranslator();
// whereString = " where " + translator.Translate(expression);
//}
//Sort
if (queryModel.QuerySort == null || queryModel.QuerySort.Count == 0)
{
var keys = KeyPropertiesCache(type);
if (keys.Count() == 0)
throw new DataException("查询对象中必须包含主键");
sortString = keys.First().Name;
}
else
{
foreach (var item in queryModel.QuerySort)
{
sortString += " " + item.Field + " " + (item.IsDesc ? "DESC" : "") + ",";
}
sortString = sortString.TrimEnd(',');
}
sql = string.Format(sql, sortString, whereString, (queryModel.PageIndex - 1) * queryModel.PageSize + 1, queryModel.PageSize * queryModel.PageIndex);
var grid = await unitOfWork.Connection.QueryMultipleAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
#if DEBUG
Trace.WriteLine(sql);
#endif
return new PageResult()
{
Result = grid.Read(),
TotalCount = grid.Read().SingleOrDefault(),
PageIndex = queryModel.PageIndex,
PageSize = queryModel.PageIndex
};
}
public static async Task IsExistsAsync(this IUnitOfWork unitOfWork, Expression> expression, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
//if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
//{
//TableName
string tableName = GetTableName(type);
sql = @"if exists(select 1 from " + tableName + @"(nolock) {0} )
select 1;
select 0 ";
// GetQueries[type.TypeHandle] = sql;
//}
string whereString = "";
//Where
if (expression != null)
{
var translator = new QueryTranslator();
whereString = " where " + translator.Translate(expression);
}
sql = string.Format(sql, whereString);
#if DEBUG
Trace.WriteLine(sql);
#endif
var r = await unitOfWork.Connection.QueryAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
return r.Single();
}
public static async Task> QueryAsync(this IUnitOfWork unitOfWork, Expression> expression, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
var name = GetTableName(type);
sql = "select * from " + name + "(nolock) where {0}";
var translator = new QueryTranslator();
string where = translator.Translate(expression);
sql = string.Format(sql, where);
#if DEBUG
Trace.WriteLine(sql);
#endif
return await unitOfWork.Connection.QueryAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
}
public static async Task> QueryAsync(this IUnitOfWork unitOfWork, Expression> expression, string customSql, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql;
//if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
//{
var name = GetTableName(type);
sql = "select * from ( " + customSql + " ) " + name + " where {0}";
//GetQueries[type.TypeHandle] = sql;
//}
var translator = new QueryTranslator();
string where = translator.Translate(expression);
sql = string.Format(sql, where);
#if DEBUG
Trace.WriteLine(sql);
#endif
return await unitOfWork.Connection.QueryAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
}
public static async Task CountAsync(this IUnitOfWork unitOfWork, Expression> expression, int? commandTimeout = null) where T : class
{
var type = typeof(T);
string sql = string.Empty;
//if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
//{
var name = GetTableName(type);
sql = "select count(0) from " + name + "(nolock) where {0}";
var translator = new QueryTranslator();
string where = translator.Translate(expression);
sql = string.Format(sql, where);
if (unitOfWork.DataPermission != null && unitOfWork.DataPermission.Count > 0)
{//存在数据权限
var allProperties = TypePropertiesCache(typeof(T));
for (var i = 0; i < allProperties.Count(); i++)
{
var property = allProperties.ElementAt(i);
var identity = property.GetCustomAttribute();
if (identity == null)
continue;
var permission = unitOfWork.DataPermission.Find(x => x.PermissioinType == identity.PermissionType.GetValue());
if (permission == null)
continue;
if (string.IsNullOrWhiteSpace(permission.Value))
continue;
sql += " AND {0} IN ({1})".Formater(property.Name, permission.Value.TrimEnd(','));
}
}
#if DEBUG
Trace.WriteLine(sql);
#endif
return await unitOfWork.Connection.ExecuteScalarAsync(sql, transaction: unitOfWork.Transaction, commandTimeout: commandTimeout);
}
///
/// 执行存储过程
///
///
/// 存储过程名称
/// 参数列表
///
///
public static async Task