Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2019
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.36 KB | None | 0 0
  1. namespace LinqToDB.Extensions.SqlServer
  2. {
  3. using LinqToDB;
  4. using LinqToDB.Data;
  5. using LinqToDB.Mapping;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Linq.Expressions;
  10. using System.Reflection;
  11. using System.Runtime.CompilerServices;
  12. using System.Text;
  13.  
  14. public static class Linq2DBExtensions
  15. {
  16. public static int TableValueConstructorMaxParameterCount = 100;
  17. public static int TableValueConstructorMaxNonParameterCount = 2000;
  18.  
  19. private static readonly Type[] _parameterTypes =
  20. {
  21. typeof(string),
  22. typeof(char),
  23. typeof(char?),
  24. typeof(Guid),
  25. typeof(Guid?),
  26. typeof(DateTime),
  27. typeof(DateTime?),
  28. typeof(DateTimeOffset),
  29. typeof(DateTimeOffset?),
  30. typeof(TimeSpan),
  31. typeof(TimeSpan?)
  32. };
  33.  
  34. public static Expression<Func<T, object>>[] ToPK<T>(this IEnumerable<T> source,
  35. params Expression<Func<T, object>>[] expressions) where T : class
  36. => expressions;
  37.  
  38. public static IQueryable<T> ToQueryable<T>(this IEnumerable<T> numbers, IDataContext db,
  39. params Expression<Func<T, object>>[] pk)
  40. where T : class
  41. => ToQueryable(numbers, db, null, null, false, pk);
  42.  
  43. public static IQueryable<T> ToQueryable<T>(this IEnumerable<T> numbers, IDataContext db,
  44. string tableName = null,
  45. string databaseName = null,
  46. bool dropFirst = false,
  47. Expression<Func<T, object>>[] pk = null)
  48. where T : class
  49. {
  50.  
  51. var listData = (numbers as IList<T>) ?? numbers.ToList();
  52. var rowCount = listData.Count;
  53.  
  54. var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
  55.  
  56. if (rowCount > TableValueConstructorMaxNonParameterCount ||
  57. TableValueConstructorMaxParameterCount <
  58. (rowCount * properties.Count(x => _parameterTypes.Contains(x.PropertyType))))
  59. {
  60. return CreateTempTable(db, listData, pk, tableName, databaseName, dropFirst);
  61. }
  62.  
  63. return BuildTableValuedConstructor(db, listData, rowCount, properties);
  64. }
  65.  
  66. public static IQueryable<T> ToTableValuedConstructor<T>(this IEnumerable<T> numbers, IDataContext db)
  67. where T : class
  68. {
  69. var listData = (numbers as IList<T>) ?? numbers.ToList();
  70. var rowCount = listData.Count;
  71. var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
  72.  
  73. return BuildTableValuedConstructor(db, listData, rowCount, properties);
  74. }
  75.  
  76. private static IQueryable<T> BuildTableValuedConstructor<T>(IDataContext db, IList<T> listData, int rowCount,
  77. PropertyInfo[] properties)
  78. where T : class
  79. {
  80. var builder = db.CreateSqlProvider();
  81. var fieldNames = string.Join(", ", properties.Select(x =>
  82. builder.Convert(x.Name, SqlProvider.ConvertType.NameToQueryField)));
  83.  
  84. if (rowCount == default(int))
  85. {
  86. return db.FromSql<T>($"(SELECT {fieldNames} " +
  87. $"FROM (VALUES ({string.Join(", ", Enumerable.Range(0, properties.Length).Select(x => "(0)"))}))" +
  88. $" AS b({fieldNames}) WHERE 0 <> 0)", new object[0]);
  89. }
  90.  
  91. var paramList = new List<DataParameter>();
  92. var data = BuildTableValuedData(db.MappingSchema, listData, properties, paramList);
  93. return db.FromSql<T>($"(SELECT {fieldNames} FROM (VALUES {data}) AS b({fieldNames}))", paramList.ToArray());
  94. }
  95.  
  96. public static IQueryable<T> ToTempTable<T>(this IEnumerable<T> numbers, IDataContext db,
  97. params Expression<Func<T, object>>[] pk)
  98. where T : class
  99. => CreateTempTable(db, numbers, pk, null, null, false);
  100.  
  101. public static IQueryable<T> ToTempTable<T>(this IEnumerable<T> numbers, IDataContext db,
  102. Expression<Func<T, object>>[] pk = null,
  103. string tableName = null,
  104. string databaseName = null,
  105. bool dropFirst = false)
  106. where T : class
  107. => CreateTempTable(db, numbers, pk, tableName, databaseName, dropFirst);
  108.  
  109.  
  110. public static IQueryable<T> CreateTempTable<T>(this IDataContext db, IEnumerable<T> numbers,
  111. Expression<Func<T, object>>[] pk = null,
  112. string tableName = null,
  113. string databaseName = null,
  114. bool dropFirst = false)
  115. where T : class
  116. {
  117. var tType = typeof(T);
  118.  
  119. tableName = !string.IsNullOrEmpty(tableName) ? tableName : '#' + Guid.NewGuid().ToString();
  120. if (tableName[0] != '#')
  121. {
  122. tableName = '#' + tableName;
  123. }
  124.  
  125. if (dropFirst)
  126. {
  127. DropTableIfExists<T>(db, tableName);
  128. }
  129.  
  130. if (pk != null && (tType.IsAnonymousType() || !db.MappingSchema.GetEntites().Contains(tType)))
  131. {
  132. var entityMapper = db.MappingSchema.GetFluentMappingBuilder().Entity<T>();
  133. for (int i = 0; i < pk.Length; i++)
  134. {
  135. var pkField = pk[i];
  136. var fieldSetter = entityMapper.Property(pkField).IsPrimaryKey(i);
  137.  
  138. if (DoesExpressionReturnString(pkField))
  139. {
  140. fieldSetter.HasLength(450).IsNullable(false);
  141. }
  142.  
  143. }
  144. }
  145.  
  146. var temp = db.CreateTable<T>(tableName, databaseName);
  147. temp.BulkCopy(new BulkCopyOptions { BulkCopyType = BulkCopyType.MultipleRows }, numbers);
  148. return temp;
  149. }
  150.  
  151. private static readonly Type StringType = typeof(string);
  152. private static bool DoesExpressionReturnString<T>(Expression<Func<T, object>> expr)
  153. {
  154. var unary = expr.Body as UnaryExpression;
  155. MemberExpression memberExpr = null;
  156.  
  157. if (unary != null)
  158. {
  159. memberExpr = unary.Operand as MemberExpression;
  160. }
  161.  
  162. if (memberExpr == null)
  163. {
  164. memberExpr = expr.Body as MemberExpression;
  165. }
  166.  
  167. if (memberExpr == null)
  168. {
  169. throw new ArgumentException("PK specified is invalid");
  170. }
  171.  
  172. var propInfo = (PropertyInfo)memberExpr.Member;
  173.  
  174. return propInfo.PropertyType == StringType;
  175. }
  176.  
  177.  
  178.  
  179.  
  180. private class SqlServerObject
  181. {
  182. public int? ObjectId { get; set; }
  183. }
  184.  
  185. private static bool DropTableIfExists<T>(IDataContext db, string tableName)
  186. {
  187. if (string.IsNullOrEmpty(tableName) || !tableName.StartsWith("#", StringComparison.Ordinal))
  188. {
  189. return false;
  190. }
  191.  
  192. var recordCount = db.FromSql<SqlServerObject>("SELECT OBJECT_ID({0}) " +
  193. $"AS {GetColumnName(nameof(SqlServerObject.ObjectId), db)}",
  194. new DataParameter("name", "tempdb.." + GetTableName(db, tableName), DataType.NVarChar))
  195. .FirstOrDefault();
  196.  
  197. if (recordCount.ObjectId.HasValue)
  198. {
  199. db.DropTable<T>(tableName);
  200. return true;
  201. }
  202.  
  203. return false;
  204. }
  205.  
  206.  
  207. private static string GetTableName(IDataContext db, string tableName, string schema = null, string database = null)
  208. => db.CreateSqlProvider()
  209. .ConvertTableName(new StringBuilder(), database, schema, tableName).ToString();
  210.  
  211. private static string GetColumnName(string property, IDataContext db)
  212. => db.CreateSqlProvider()
  213. .Convert(property, SqlProvider.ConvertType.NameToQueryField).ToString();
  214.  
  215.  
  216.  
  217.  
  218. public static long BulkLoad<T>(this IDataContext db, IEnumerable<T> source,
  219. BulkCopyType bulkCopyType = BulkCopyType.MultipleRows)
  220. where T : class
  221. {
  222. var iTable = db.GetTable<T>();
  223. var result = iTable.BulkCopy(new BulkCopyOptions { BulkCopyType = bulkCopyType }, source);
  224. return result.RowsCopied;
  225. }
  226.  
  227.  
  228. private static string BuildTableValuedData<T>(MappingSchema mappingSchema, IEnumerable<T> data, PropertyInfo[] properties, List<DataParameter> parameters) where T : class
  229. {
  230. var param = Expression.Parameter(typeof(T), "p");
  231. var paramIndex = Expression.Parameter(typeof(int), "index");
  232. var paramSchema = Expression.Parameter(typeof(List<DataParameter>), "dataParams");
  233. var paramMapper = Expression.Parameter(typeof(MappingSchema), "dataSchema");
  234.  
  235.  
  236. var exps = new List<Expression>();
  237.  
  238. foreach (var property in properties)
  239. {
  240. Expression exp = Expression.MakeMemberAccess(param, property);
  241. exp = Expression.Convert(exp, typeof(object));
  242. exps.Add(exp);
  243. }
  244.  
  245. var arrayExp = Expression.NewArrayInit(typeof(object), exps.ToArray());
  246.  
  247.  
  248. Expression callExpr = Expression.Call(
  249. typeof(Linq2DBExtensions).GetMethod(nameof(Linq2DBExtensions.DataToString), BindingFlags.Static | BindingFlags.NonPublic),
  250. paramMapper,
  251. paramIndex,
  252. paramSchema,
  253. Expression.Constant(properties.Select(x => new PropertyData
  254. {
  255. IsParameterNeeded = _parameterTypes.Contains(x.PropertyType),
  256. Name = x.Name,
  257. Type = x.PropertyType,
  258. DataType = DataType.Undefined
  259. }).ToArray()),
  260. arrayExp);
  261.  
  262. var lambda = Expression.Lambda<Func<MappingSchema, int, List<DataParameter>, T, string>>(callExpr, paramMapper, paramIndex, paramSchema, param);
  263. var func = lambda.Compile();
  264.  
  265. return string.Concat("(", string.Join("), (", data.Select((x, i) => func(mappingSchema, i, parameters, x))), ")");
  266. }
  267.  
  268. private class PropertyData
  269. {
  270. public Type Type { get; set; }
  271. public string Name { get; set; }
  272. public bool IsParameterNeeded { get; set; }
  273. public DataType DataType { get; set; }
  274. }
  275.  
  276. private static string DataToString(MappingSchema mappingSchema, int rowIndex, List<DataParameter> dataParams, PropertyData[] needsParameter, params object[] values)
  277. {
  278. var data = new List<string>();
  279. var sb = new StringBuilder();
  280. for (int i = 0; i < values.Length; i++)
  281. {
  282. var value = values[i];
  283. if (needsParameter[i].IsParameterNeeded)
  284. {
  285. var parameter = new DataParameter(needsParameter[i].Name + "_" + rowIndex, value);
  286. data.Add(value == null ? "NULL" : "{" + dataParams.Count + "}");
  287. dataParams.Add(parameter);
  288. }
  289. else
  290. {
  291. sb.Clear();
  292. data.Add(mappingSchema.ValueToSqlConverter.Convert(sb, value).ToString());
  293. }
  294. }
  295.  
  296. return string.Join(", ", data);
  297. }
  298.  
  299. public static IQueryable<Dual> Dual(this IDataContext db)
  300. {
  301. var builder = db.CreateSqlProvider();
  302. var fieldName = builder.Convert("Discard", SqlProvider.ConvertType.NameToQueryField);
  303. var sql = $"(SELECT 1 AS {fieldName})";
  304.  
  305. return db.FromSql<Dual>(sql);
  306. }
  307.  
  308.  
  309. private static bool IsAnonymousType(this Type type)
  310. {
  311. if (type == null)
  312. {
  313. throw new ArgumentNullException(nameof(type));
  314. }
  315.  
  316. // HACK: The only way to detect anonymous types right now.
  317. return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
  318. && type.IsGenericType && type.Name.Contains("AnonymousType")
  319. && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
  320. && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
  321. }
  322. }
  323.  
  324. public class Dual
  325. {
  326. public bool Discard { get; set; }
  327. }
  328. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement