static bool IsSupportedType(Type propertyType)
 {
     return(Types.IsStructured(propertyType) || Types.TypeToDbType.ContainsKey(propertyType) || propertyType.IsEnum);
 }
        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());
        }