static LambdaExpression CreateMappingLambda(Type typeT, List <Mapping> mapping)
        {
            var result          = Expression.Parameter(typeof(SqlDataRecord), "rec");
            var metaDataParam   = Expression.Parameter(typeof(SqlMetaData[]), "metaData");
            var item            = Expression.Parameter(typeT, "item");
            var constructorInfo = typeof(SqlDataRecord).GetConstructor(new[] { typeof(SqlMetaData[]) });
            var lines           = new List <Expression>
            {
                Expression.Assign(result, Expression.New(constructorInfo, metaDataParam))
            };
            var propertiesAndFields = Types.ReadablePropertiesAndFieldsDictionary(typeT);

            var setNullMethod = typeof(SqlDataRecord).GetMethod("SetDBNull", new[] { typeof(int) });

            Contract.Assert(setNullMethod != null);

            foreach (var map in mapping)
            {
                var col         = (Column)map.To;
                var setValueExp = SetValue(result, item, map.From, col);
                if (setValueExp == null)
                {
                    continue;
                }

                if (Types.CanBeNull(map.From.Type))
                {
                    lines.Add(Expression.IfThenElse(
                                  Expression.Equal(Expression.PropertyOrField(item, map.From.Name), Expression.Constant(null)),
                                  Expression.Call(result, setNullMethod, Expression.Constant(col.Ordinal)),
                                  setValueExp
                                  ));
                }
                else
                {
                    lines.Add(setValueExp);
                }
            }
            lines.Add(result);
            var block            = Expression.Block(new[] { result }, lines);
            var func             = typeof(Func <, ,>).MakeGenericType(typeof(SqlMetaData[]), typeT, typeof(SqlDataRecord));
            var lambdaExpression = Expression.Lambda(func, block, metaDataParam, item);

            return(lambdaExpression);
        }
        static Action <DbCommand, object> CreateAddParametersAction(Type type)
        {
            var obj        = Expression.Parameter(typeof(object), "obj");
            var parameters = Expression.Parameter(type, "parameters");
            var cmd        = Expression.Parameter(typeof(DbCommand), "cmd");
            var dataParam  = Expression.Parameter(typeof(DbParameter), "dataParam");
            var lines      = new List <Expression>
            {
                Expression.Assign(parameters, Expression.Convert(obj, type))
            };
            var createParameter = typeof(DbCommand).GetMethod("CreateParameter", Type.EmptyTypes);

            foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var propertyType = prop.PropertyType;
                if (!IsSupportedType(propertyType))
                {
                    continue;
                }
                lines.Add(Expression.Assign(dataParam, Expression.Call(cmd, createParameter)));
                lines.Add(Expression.Assign(Expression.Property(dataParam, "ParameterName"), Expression.Constant("@" + prop.Name)));

                if (Types.IsStructured(propertyType))
                {
                    if (propertyType != typeof(TableType))
                    {
                        throw new NotSupportedException($"Parameter {dataParam.Name} implements {nameof(IEnumerable<SqlDataRecord>)} but type name is unknown.  Please wrap parameter by calling {nameof(Extensions.WithTypeName)}");
                    }

                    lines.Add(Expression.IfThen(
                                  Expression.Not(Expression.TypeIs(cmd, typeof(DbCommand))),
                                  Expression.Throw(Expression.New(typeof(NotSupportedException).GetConstructor(new[] { typeof(string) }), Expression.Constant("Structured parameters are supported only for SqlCommand")))
                                  ));
                    lines.Add(Expression.Assign(Expression.Property(Expression.Convert(dataParam, typeof(SqlParameter)), "SqlDbType"), Expression.Constant(SqlDbType.Structured)));
                    lines.Add(Expression.Assign(Expression.Property(Expression.Convert(dataParam, typeof(SqlParameter)), "TypeName"), Expression.Property(Expression.Property(parameters, prop.Name), "TypeName")));
                }
                else if (propertyType.IsEnum)
                {
                    lines.Add(Expression.Assign(Expression.Property(dataParam, "DbType"), Expression.Constant(DbType.Int32)));
                }
                else
                {
                    lines.Add(Expression.Assign(Expression.Property(dataParam, "DbType"), Expression.Constant(Types.TypeToDbType[propertyType])));
                }

                if (Types.CanBeNull(propertyType))
                {
                    lines.Add(Expression.IfThenElse(
                                  Expression.Equal(Expression.Property(parameters, prop.Name), Expression.Constant(null)),
                                  Expression.Assign(Expression.Property(dataParam, "Value"), Expression.Convert(Expression.Field(null, typeof(DBNull), "Value"), typeof(object))),
                                  Expression.Assign(Expression.Property(dataParam, "Value"), PropertyValue(parameters, prop))
                                  ));
                }
                else
                {
                    lines.Add(Expression.Assign(Expression.Property(dataParam, "Value"), PropertyValue(parameters, prop)));
                }

                lines.Add(Expression.Call(Expression.Property(cmd, "Parameters"), typeof(DbParameterCollection).GetMethod("Add", new[] { typeof(object) }), dataParam));
            }
            var block = Expression.Block(new[] { dataParam, parameters }, lines);

            return(Expression.Lambda <Action <DbCommand, object> >(block, cmd, obj).Compile());
        }