コード例 #1
0
        public DocumentReaderExpressionBuilder(DocumentMap map, ITypeHandlerRegistry typeHandlers)
        {
            type              = map.Type;
            this.map          = map;
            this.typeHandlers = typeHandlers;

            var contextType = typeof(DocumentReaderContext);

            dataReaderArgument = Expression.Parameter(typeof(DbDataReader), "reader");
            contextArgument    = Expression.Parameter(contextType, "context");
            result             = DeclareLocal(map.Type, "result");
            deserializeAsLocal = DeclareLocal(typeof(Type), "deserializeAsType");

            columnField                 = contextType.GetField(nameof(DocumentReaderContext.Column));
            deserializeTextMethod       = contextType.GetMethod(nameof(DocumentReaderContext.DeserializeText), BindingFlags)?.MakeGenericMethod(type);
            deserializeCompressedMethod = contextType.GetMethod(nameof(DocumentReaderContext.DeserializeCompressed), BindingFlags)?.MakeGenericMethod(type);
            resolveTypeMethod           = contextType.GetMethod(nameof(DocumentReaderContext.ResolveType), BindingFlags);
            selectPreferredResultMethod = contextType.GetMethod(nameof(DocumentReaderContext.SelectPreferredResult), BindingFlags)?.MakeGenericMethod(type);
            propertyHandlerWriteMethod  = typeof(IPropertyHandler).GetMethod(nameof(IPropertyHandler.Write), BindingFlags);

            if (columnField == null || deserializeTextMethod == null || deserializeCompressedMethod == null || resolveTypeMethod == null || selectPreferredResultMethod == null || propertyHandlerWriteMethod == null)
            {
                throw new InvalidOperationException("Could not find one or more required methods.");
            }
        }
コード例 #2
0
 public virtual void ContributeTo(IDbCommand command, ITypeHandlerRegistry typeHandlers, DocumentMap mapping = null)
 {
     command.CommandType = CommandType;
     foreach (var pair in this)
     {
         ContributeParameter(command, typeHandlers, pair.Key, pair.Value, mapping);
     }
 }
コード例 #3
0
        protected virtual void ContributeParameter(IDbCommand command, ITypeHandlerRegistry typeHandlers, string name, object value, DocumentMap mapping = null)
        {
            if (value == null)
            {
                command.Parameters.Add(new SqlParameter(name, DBNull.Value));
                return;
            }

            var typeHandler = typeHandlers.Resolve(value.GetType());

            if (typeHandler != null)
            {
                var p = new SqlParameter(name, SqlDbType.NVarChar);
                typeHandler.WriteDatabase(p, value);
                command.Parameters.Add(p);
                return;
            }

            if (value is TableValuedParameter tvp && command is SqlCommand sqlCommand)
            {
                var p = sqlCommand.Parameters.Add(name, SqlDbType.Structured);
                p.Value    = tvp.DataRecords;
                p.TypeName = tvp.TypeName;
                return;
            }

            if (value is IEnumerable && (value is string) == false && (value is byte[]) == false)
            {
                var inClauseNames = new List <string>();
                var i             = 0;

                var inClauseValues = ((IEnumerable)value).Cast <object>().ToList();

                ListExtender.ExtendListRepeatingLastValue(inClauseValues);

                foreach (var inClauseValue in inClauseValues)
                {
                    i++;
                    var inClauseName = name + "_" + i;
                    inClauseNames.Add(inClauseName);
                    ContributeParameter(command, typeHandlers, inClauseName, inClauseValue);
                }

                if (i == 0)
                {
                    var inClauseName = name + "_" + i;
                    inClauseNames.Add(inClauseName);
                    ContributeParameter(command, typeHandlers, inClauseName, null);
                }

                var originalParameter     = Regex.Escape("@" + name.TrimStart('@')) + @"(?=[^\w\$@#_]|$)";
                var replacementParameters = "(" + string.Join(", ", inClauseNames.Select(x => "@" + x)) + ")";
                command.CommandText = Regex.Replace(command.CommandText, originalParameter, match => replacementParameters, RegexOptions.IgnoreCase);
                return;
            }

            var columnType = DatabaseTypeConverter.AsDbType(value.GetType());

            if (columnType == null)
            {
                throw new InvalidOperationException($"Cannot map type '{value.GetType().FullName}' to a DbType. Consider providing a custom ITypeHandler.");
            }

            var param = new SqlParameter();

            param.ParameterName = name;
            param.DbType        = columnType.Value;
            param.Value         = value;

            if (columnType == DbType.String && value is string text)
            {
                var size = GetBestSizeBucket(text);
                if (size > 0)
                {
                    param.Size = size;
                }
            }

            // To assist SQL's query plan caching, assign a parameter size for our
            // common id lookups where possible.
            if (mapping != null &&
                mapping.IdColumn != null &&
                mapping.IdColumn.MaxLength > 0 &&
                columnType == DbType.String &&
                string.Equals(name, mapping.IdColumn.ColumnName, StringComparison.OrdinalIgnoreCase))
            {
                if (mapping.IdColumn.MaxLength != null)
                {
                    param.Size = mapping.IdColumn.MaxLength.Value;
                }
            }

            if (columnType == DbType.String && mapping != null)
            {
                var indexed   = mapping.WritableIndexedColumns();
                var columnMap = indexed.FirstOrDefault(i => string.Equals(i.ColumnName, param.ParameterName, StringComparison.OrdinalIgnoreCase));
                if (columnMap != null && columnMap.MaxLength != null)
                {
                    param.Size = columnMap.MaxLength.Value;
                }
            }

            command.Parameters.Add(param);
        }
コード例 #4
0
 public DbCommand CreateCommand(DbConnection connection, DbTransaction transaction, string statement,
                                CommandParameterValues args, ITypeHandlerRegistry typeHandlers, DocumentMap mapping = null, TimeSpan?commandTimeout = null)
 {
     return(new ChaosSqlCommand(wrappedFactory.CreateCommand(connection, transaction, statement, args, typeHandlers, mapping, commandTimeout), chaosFactor));
 }
コード例 #5
0
        public DbCommand CreateCommand(DbConnection connection, DbTransaction transaction, string statement, CommandParameterValues args, ITypeHandlerRegistry typeHandlers, DocumentMap mapping = null, TimeSpan?commandTimeout = null)
        {
            var command = connection.CreateCommand();

            try
            {
                command.CommandTimeout = (int)(commandTimeout ?? DefaultCommandTimeout).TotalSeconds;
                command.CommandText    = statement;
                command.Transaction    = transaction;
                args?.ContributeTo(command, typeHandlers, mapping);
                return(command);
            }
            catch
            {
                command.Dispose();
                throw;
            }
        }
コード例 #6
0
ファイル: ExpressionHelper.cs プロジェクト: matkoch/Nevermore
        public static Expression GetValueFromReaderAsType(Expression reader, Expression index, Type propertyType, ITypeHandlerRegistry customTypeHandlerRegistry)
        {
            const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;

            // For value types where we know they cannot be null
            // E.g.,   foo.Age = reader.GetInt32(i)
            var neverNull = new Func <string, Expression>(nameOfGetMethod => Expression.Call(reader, typeof(IDataRecord).GetMethod(nameOfGetMethod, bindingFlags), index));

            // For reference types
            // E.g.,   foo.Name = reader.IsDBNull(i) ? null : reader.GetString(i);
            var maybeNull = new Func <string, Expression>(nameOfGetMethod =>
                                                          Expression.Condition(
                                                              Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), bindingFlags), index),
                                                              Expression.Constant(null, propertyType),
                                                              Expression.Call(reader, typeof(IDataRecord).GetMethod(nameOfGetMethod, bindingFlags), index)));

            // For nullable types (whether the method return is slightly different, and needs to be cast)
            // E.g.,   foo.LuckyNumber = reader.IsDBNull(i) ? null : (int?)reader.GetInt32(i);
            var maybeNullWithCast = new Func <string, Expression>(nameOfGetMethod =>
                                                                  Expression.Condition(
                                                                      Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), bindingFlags), index),
                                                                      Expression.Constant(null, propertyType),
                                                                      Expression.Convert(
                                                                          Expression.Call(reader, typeof(IDataRecord).GetMethod(nameOfGetMethod, bindingFlags), index),
                                                                          propertyType)));

            // Optimize for common types
            if (propertyType == typeof(string))
            {
                return(maybeNull(nameof(IDataRecord.GetString)));
            }
            if (propertyType == typeof(DateTime))
            {
                return(neverNull(nameof(IDataRecord.GetDateTime)));
            }
            if (propertyType == typeof(DateTime?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetDateTime)));
            }
            if (propertyType == typeof(int))
            {
                return(neverNull(nameof(IDataRecord.GetInt32)));
            }
            if (propertyType == typeof(int?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetInt32)));
            }
            if (propertyType == typeof(decimal))
            {
                return(neverNull(nameof(IDataRecord.GetDecimal)));
            }
            if (propertyType == typeof(decimal?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetDecimal)));
            }
            if (propertyType == typeof(char))
            {
                return(neverNull(nameof(IDataRecord.GetChar)));
            }
            if (propertyType == typeof(char?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetChar)));
            }
            if (propertyType == typeof(long))
            {
                return(neverNull(nameof(IDataRecord.GetInt64)));
            }
            if (propertyType == typeof(long?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetInt64)));
            }
            if (propertyType == typeof(short))
            {
                return(neverNull(nameof(IDataRecord.GetInt16)));
            }
            if (propertyType == typeof(short?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetInt16)));
            }
            if (propertyType == typeof(Guid))
            {
                return(neverNull(nameof(IDataRecord.GetGuid)));
            }
            if (propertyType == typeof(Guid?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetGuid)));
            }
            if (propertyType == typeof(float))
            {
                return(neverNull(nameof(IDataRecord.GetFloat)));
            }
            if (propertyType == typeof(float?))
            {
                return(maybeNullWithCast(nameof(IDataRecord.GetFloat)));
            }

            // Enum or Nullable<Enum>
            if (propertyType.IsEnum || (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>) && propertyType.GetGenericArguments()[0].IsEnum))
            {
                var underlyingEnumType = propertyType.IsEnum ? propertyType : propertyType.GetGenericArguments()[0];

                // reader.IsDBNull(0)
                //     ? default(TEnum?)
                //     : (TEnum?)(reader.GetFieldType(0) == typeof(string)
                //         ? (Enum.Parse<TEnum>(reader.GetString(0)))
                //         : ((TEnum)reader.GetValue(0)));
                return(Expression.Condition(
                           Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), bindingFlags), index),
                           Expression.Default(propertyType),
                           Expression.Convert(
                               Expression.Condition(
                                   Expression.Equal(
                                       Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetFieldType), bindingFlags), index),
                                       Expression.Constant(typeof(string), typeof(Type))),
                                   Expression.Call(null,
                                                   typeof(Enum).GetMethods(BindingFlags.Static | BindingFlags.Public).Single(m => m.Name == "Parse" && m.IsGenericMethod && m.GetParameters().Length == 2).MakeGenericMethod(underlyingEnumType),
                                                   Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetString), bindingFlags), index),
                                                   Expression.Constant(true) // ignoreCase
                                                   ),
                                   Expression.Convert(
                                       Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetValue), bindingFlags), index),
                                       underlyingEnumType)),
                               propertyType
                               )));
            }

            // We allow custom type handlers, but not for the primitive types shown above - we deal with so many
            // of these that we want them to be fast!
            var typeHandler = customTypeHandlerRegistry.Resolve(propertyType);

            if (typeHandler != null)
            {
                var handler = Expression.Constant(typeHandler, typeof(ITypeHandler));
                return(Expression.Convert(Expression.Call(handler, typeof(ITypeHandler).GetMethod(nameof(ITypeHandler.ReadDatabase), bindingFlags), reader, index), propertyType));
            }

            // Fallback:
            // E.g.,   foo.SomeValue = reader.IsDBNull(i) ? default(float?) : (float?) reader[i];
            return(Expression.Condition(
                       Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), bindingFlags), index),
                       Expression.Default(propertyType),
                       Expression.Convert(
                           Expression.Call(reader, typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetValue), bindingFlags), index),
                           propertyType)));
        }