SHARE
TWEET

Untitled

a guest Mar 23rd, 2019 53 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top