Mri*_*boj 6 c# sql-server datatable stored-procedures dapper
我试图创建一个通用方法,该方法可以在运行时从类中读取参数名称和值,并为Dapper查询执行创建参数集合。意识到直到所有参数都是输入类型为止,它都能很好地工作,但是如果我必须添加Output / ReturnValue类型参数,则需要使用DynamicParameters,否则我将无法获取Output / ReturnValue参数的值
SP具有以下参数:
PersonList - TableValued - Input
TestOutput - Int - Output
我无法使以下代码起作用:
var dynamicParameters = new DynamicParameters();
dynamicParameters.Add("PersonList", <DataTable PersonList>);
dynamicParameters.Add("TestOutput", 0, Dbtype.Int32, ParameterDirection.Output);
例外是:
System.Data.SqlClient.SqlException:传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。参数1(“ @PersonList”):数据类型0x62(sql_variant)的类型特定元数据的类型无效。
据我了解,问题是没有有效的DbType可用于向动态参数添加TVP,因为我没有使用SqlDbType,因此DbType中没有替代SqlDbType.Structured的内容。
解决问题的任何指针或解决方法
小智 9
首先在数据库中创建一个用户定义的表类型
CREATE TYPE udtt_PersonList AS TABLE 
(
    ...
)
GO
在你的代码中
var dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@PersonList", PersonList.AsTableValuedParameter("[dbo].[udtt_PersonList]"));
dynamicParameters.Add("TestOutput", 0, Dbtype.Int32, ParameterDirection.Output);
据我了解,此要求不受开箱即用的支持,我可能需要编写特定的帮助程序。我已经使用自定义基抽象类TypeMap(可以由各种提供程序扩展)来解决它,以实现 API,这不是使用 Dapper 开箱即用的,我正在粘贴与 SQL-Server 相关的实现,类似可以为其他 ADO.Net 兼容提供商完成:
namespace Dapper
{
    #region NameSpaces
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    #endregion
        /// <summary>
        ///     Type Map class for database provider specific code
        /// </summary>
        internal abstract class TypeMap
        {
            /// <summary>
            /// Only Non Input Parameters collection
            /// </summary>
            public abstract Dictionary<string, object> NonInputParameterCollection { get; set; } 
            /// <summary>
            /// Method to execute the DML via TypeMap
            /// </summary>
            /// <param name="connection"></param>
            /// <param name="sql"></param>
            /// <param name="commandType"></param>
            /// <param name="dapperParams"></param>
            /// <returns></returns>
            public abstract int Execute(IDbConnection connection, 
                                        string sql, 
                                        CommandType commandType,
                                        IEnumerable<DapperParam> dapperParams );
            /// <summary>
            /// Method to execute the Select to fetch IEnumerable via TypeMap
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="connection"></param>
            /// <param name="sql"></param>
            /// <param name="commandType"></param>
            /// <param name="dapperParams"></param>
            /// <returns></returns>
            public abstract IEnumerable<T> Query<T>(IDbConnection connection,
                                                    string sql,
                                                    CommandType commandType,
                                                    IEnumerable<DapperParam> dapperParams) where T : new();
            /// <summary>
            /// Fetch the relevant TypeMap
            /// </summary>
            /// <param name="provider"></param>
            /// <returns></returns>
            public static TypeMap GetTypeMap(string provider)
            {
                TypeMap typeMap = null;
                switch (provider)
                {
                    case "System.Data.SqlClient":
                        typeMap = new SqlTypeMap();
                        break;
                    default:
                        // SQl Server TypeMap
                        typeMap = new SqlTypeMap();
                        break;
                }
                return (typeMap);
            }
        }
        /// <summary>
        ///     SQL Server provider type map
        /// </summary>
        internal class SqlTypeMap : TypeMap
        {
            public SqlTypeMap()
            {
                NonInputParameterCollection = new Dictionary<string, object>();
            }
            public override sealed Dictionary<string, object> NonInputParameterCollection { get; set; } 
            public override int Execute(IDbConnection connection,
                                        string sql,
                                        CommandType commandType,
                                        IEnumerable<DapperParam> dapperParams)
            {
                int returnValue = -1;
                var sqlConnection = (connection as SqlConnection) ?? new SqlConnection();
                using (sqlConnection)
                {
                    SqlCommand sqlCommand = null;
                    sqlCommand = sqlConnection.CreateCommand();
                    using (sqlCommand)
                    {
                        // public SqlParameter(string parameterName, SqlDbType dbType, int size, ParameterDirection direction, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value, string xmlSchemaCollectionDatabase, string xmlSchemaCollectionOwningSchema, string xmlSchemaCollectionName);
                        foreach (var param in dapperParams)
                        {
                            sqlCommand.Parameters.Add(new SqlParameter
                            {
                                ParameterName = param.ParamName,
                                SqlValue = param.ParamValue ?? DBNull.Value,
                                SqlDbType = TypeToSqlDbType[param.ParamType],
                                Direction = Map.DirectionMap[param.ParamDirection]
                            });
                        }
                        sqlCommand.CommandText = sql; // Assign Sql Text
                        sqlCommand.CommandType = commandType; // Assign CommandType
                        sqlCommand.Connection.Open(); // Explicitly open connection to use it with SqlCommand object
                        returnValue = sqlCommand.ExecuteNonQuery(); // Execute Query
                        foreach (SqlParameter param in sqlCommand.Parameters.Cast<SqlParameter>().Where(param => param.Direction != ParameterDirection.Input))
                            NonInputParameterCollection.Add(param.ParameterName, param.Value);
                    }
                }
                return (returnValue);
            }
            public override IEnumerable<T> Query<T>(IDbConnection connection,
                                      string sql,
                                      CommandType commandType,
                                      IEnumerable<DapperParam> dapperParams)
            {
                IEnumerable<T> returnEnumerable = null;
                var sqlConnection = (connection as SqlConnection) ?? new SqlConnection();
                using (sqlConnection)
                {
                    var sqlCommand = sqlConnection.CreateCommand();
                    using (sqlCommand)
                    {
                        foreach (var param in dapperParams)
                        {
                            sqlCommand.Parameters.Add(new SqlParameter
                            {
                                ParameterName = param.ParamName,
                                SqlValue = param.ParamValue ?? DBNull.Value,
                                SqlDbType = TypeToSqlDbType[param.ParamType],
                                Direction = Map.DirectionMap[param.ParamDirection]
                            });
                        }
                        sqlCommand.CommandText = sql; // Assign Sql Text
                        sqlCommand.CommandType = commandType; // Assign CommandType
                        var sqlDataAdapter = new SqlDataAdapter(sqlCommand);
                        var returnDataTable = new DataTable();
                        sqlDataAdapter.Fill(returnDataTable);
                        returnEnumerable = Common.ToList<T>(returnDataTable);
                        foreach (SqlParameter param in sqlCommand.Parameters.Cast<SqlParameter>()
                                                                 .Where(param => param.Direction != ParameterDirection.Input))
                            NonInputParameterCollection.Add(param.ParameterName, param.Value);
                    }
                }
                return (returnEnumerable);
            }
            /// <summary>
            ///     Data Type to Db Type mapping dictionary for SQL Server
            /// https://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
            /// </summary>
            public static readonly Dictionary<Type, SqlDbType> TypeToSqlDbType = new Dictionary<Type, SqlDbType>
            {
              // Mapping C# types to Ado.net SqlDbType enumeration
                {typeof (byte), SqlDbType.TinyInt},
                {typeof (sbyte), SqlDbType.TinyInt},
                {typeof (short), SqlDbType.SmallInt},
                {typeof (ushort), SqlDbType.SmallInt},
                {typeof (int), SqlDbType.Int},
                {typeof (uint), SqlDbType.Int},
                {typeof (long), SqlDbType.BigInt},
                {typeof (ulong), SqlDbType.BigInt},
                {typeof (float), SqlDbType.Float},
                {typeof (double), SqlDbType.Float},
                {typeof (decimal), SqlDbType.Decimal},
                {typeof (bool), SqlDbType.Bit},
                {typeof (string), SqlDbType.VarChar},
                {typeof (char), SqlDbType.Char},
                {typeof (Guid), SqlDbType.UniqueIdentifier},
                {typeof (DateTime), SqlDbType.DateTime},
                {typeof (DateTimeOffset), SqlDbType.DateTimeOffset},
                {typeof (byte[]), SqlDbType.VarBinary},
                {typeof (byte?), SqlDbType.TinyInt},
                {typeof (sbyte?), SqlDbType.TinyInt},
                {typeof (short?), SqlDbType.SmallInt},
                {typeof (ushort?), SqlDbType.SmallInt},
                {typeof (int?), SqlDbType.Int},
                {typeof (uint?), SqlDbType.Int},
                {typeof (long?), SqlDbType.BigInt},
                {typeof (ulong?), SqlDbType.BigInt},
                {typeof (float?), SqlDbType.Float},
                {typeof (double?), SqlDbType.Float},
                {typeof (decimal?), SqlDbType.Decimal},
                {typeof (bool?), SqlDbType.Bit},
                {typeof (char?), SqlDbType.Char},
                {typeof (Guid?), SqlDbType.UniqueIdentifier},
                {typeof (DateTime?), SqlDbType.DateTime},
                {typeof (DateTimeOffset?), SqlDbType.DateTimeOffset},
                {typeof (System.Data.Linq.Binary), SqlDbType.Binary},
                {typeof (IEnumerable<>), SqlDbType.Structured},
                {typeof (List<>), SqlDbType.Structured},
                {typeof (DataTable), SqlDbType.Structured},
            };
        }
        /// <summary>
        /// 
        /// </summary>
        public static class Map
    {
        /// <summary>
        /// 
        /// </summary>
        public static Dictionary<Type, DbType> TypeToDbType = new Dictionary<Type, DbType>()
        {
            {typeof (byte), DbType.Byte},
            {typeof (sbyte), DbType.Byte},
            {typeof (short), DbType.Int16},
            {typeof (ushort), DbType.Int16},
            {typeof (int), DbType.Int32},
            {typeof (uint), DbType.Int32},
            {typeof (long), DbType.Int64},
            {typeof (ulong), DbType.Int64},
            {typeof (float), DbType.Single},
            {typeof (double), DbType.Double},
            {typeof (decimal), DbType.Decimal},
            {typeof (bool), DbType.Boolean},
            {typeof (string), DbType.String},
            {typeof (char), DbType.StringFixedLength},
            {typeof (Guid), DbType.Guid},
            {typeof (DateTime), DbType.DateTime},
            {typeof (DateTimeOffset), DbType.DateTimeOffset},
            {typeof (byte[]), DbType.Binary},
            {typeof (byte?), DbType.Byte},
            {typeof (sbyte?), DbType.Byte},
            {typeof (short?), DbType.Int16},
            {typeof (ushort?), DbType.Int16},
            {typeof (int?), DbType.Int32},
            {typeof (uint?), DbType.Int32},
            {typeof (long?), DbType.Int64},
            {typeof (ulong?), DbType.Int64},
            {typeof (float?), DbType.Single},
            {typeof (double?), DbType.Double},
            {typeof (decimal?), DbType.Decimal},
            {typeof (bool?), DbType.Boolean},
            {typeof (char?), DbType.StringFixedLength},
            {typeof (Guid?), DbType.Guid},
            {typeof (DateTime?), DbType.DateTime},
            {typeof (DateTimeOffset?), DbType.DateTimeOffset},
            {typeof (System.Data.Linq.Binary), DbType.Binary}
        };
        /// <summary>
        ///     Parameter Direction for Stored Procedure
        /// </summary>
        public static readonly Dictionary<string, ParameterDirection> DirectionMap =
               new Dictionary<string, ParameterDirection>(StringComparer.InvariantCultureIgnoreCase)
            {
                {ParamDirectionConstants.Input, ParameterDirection.Input},
                {ParamDirectionConstants.Output, ParameterDirection.Output},
                {ParamDirectionConstants.InputOutput, ParameterDirection.InputOutput},
                {ParamDirectionConstants.ReturnValue, ParameterDirection.ReturnValue}
            };
    }
}
支持类和API,使上面的代码工作:
using System;
using System.Collections.Generic;
namespace Dapper
{
    public class DapperParam
    {
        /// <summary>
        ///     Parameter Type Constructor
        /// </summary>
        /// <param name="paramName"></param>
        /// <param name="paramType"></param>
        /// <param name="paramDirection"></param>
        /// <param name="paramValue"></param>
        public DapperParam(string paramName,
                        Type paramType,
                        string paramDirection,
                        object paramValue)
        {
            ParamName = paramName;
            ParamType = paramType;
            ParamDirection = paramDirection;
            ParamValue = paramValue;
        }
        /// <summary>
        ///     Parameter name
        /// </summary>
        public string ParamName { get; set; }
        /// <summary>
        ///     Parameter Type
        /// </summary>
        public Type ParamType { get; set; }
        /// <summary>
        ///     Parameter Direction
        /// </summary>
        public string ParamDirection { get; set; }
        /// <summary>
        ///     Parameter Value
        /// </summary>
        public object ParamValue { get; set; }
    }
    internal static class DataConversionMap
    {
        /// <summary>
        ///     Type conversion, handles null
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        private static object ConvertDbData(object obj, Func<object> func)
        {
            return (!Convert.IsDBNull(obj)) ? func() : null;
        }
        /// <summary>
        ///     Dictionary map to convert to a given DataType. Returns a Func of object,object.
        ///     Internally calls ConvertDbData for Data Type conversion
        /// </summary>
        public static readonly Dictionary<Type, Func<object, object>> Map =
            new Dictionary<Type, Func<object, object>>
            {
                {
                    typeof(Int16),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToInt16(objectValue))
                },
                {
                    typeof(Int32),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToInt32(objectValue))
                },
                {
                    typeof(Int64),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToInt64(objectValue))
                },
                {
                    typeof(Boolean),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToBoolean(objectValue))
                },
                {
                    typeof(string),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToString(objectValue))
                },
                {
                   typeof(DateTime), objectValue =>
                        ConvertDbData(objectValue, () =>
                        {
                            DateTime dateTime = Convert.ToDateTime(objectValue);
                            if (dateTime.TimeOfDay.Equals(TimeSpan.Zero))
                                return dateTime.ToShortDateString();
                            return dateTime.ToString("MM/dd/yyyy HH:mm");
                        })
                },
                {
                    typeof(Byte),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToByte(objectValue))
                },
                {
                    typeof(Double),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToDouble(objectValue))
                },
                {
                    typeof(Decimal),
                    objectValue => ConvertDbData(objectValue, () => Convert.ToDecimal(objectValue))
                },
                {
                    typeof(TimeSpan),
                    objectValue => ConvertDbData(objectValue, () => TimeSpan.Parse(objectValue.ToString()))
                },
                {
                    typeof(Guid),
                    objectValue => ConvertDbData(objectValue, () => new Guid(objectValue.ToString()))
                },
                {
                    typeof(Byte[]),
                    objectValue => ConvertDbData(objectValue, () => (Byte[])(objectValue))
                }
            };
    }
}
常用API
public static class Common
    {
        /// <summary>
        ///  Convert IEnumerable<T> to DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static DataTable CreateTable<T>(this IEnumerable<T> collection)
        {
            // Fetch the type of List contained in the ParamValue
            var tableType = typeof(T);
            // Create DataTable which will contain data from List<T>
            var dataTable = new DataTable();
            // Fetch the Type fields count
            int columnCount = tableType.GetProperties().Count();
            var columnNameMappingDictionary = new Dictionary<string, string>();
            // Create DataTable Columns using table type field name and their types
            // Traversing through Column Collection
            for (int counter = 0; counter < columnCount; counter++)
            {
                var propertyInfo = tableType.GetProperties()[counter];
                var parameterAttribute = propertyInfo.GetParameterAttribute();
                string columnName = (parameterAttribute != null) ? parameterAttribute.Name : propertyInfo.Name;
                columnNameMappingDictionary.Add(propertyInfo.Name,
                    (parameterAttribute != null) ? parameterAttribute.Name : propertyInfo.Name);
                dataTable.Columns.Add(columnName, tableType.GetProperties()[counter].PropertyType);
            }
            // Return parameter with null value
            if (collection == null)
                return dataTable;
            // Traverse through number of entries / rows in the List
            foreach (var item in collection)
            {
                // Create a new DataRow
                DataRow dataRow = dataTable.NewRow();
                // Traverse through type fields or column names
                for (int counter = 0; counter < columnCount; counter++)
                {
                    // Fetch Column Name
                    string columnName = columnNameMappingDictionary[tableType.GetProperties()[co
        
          
        
    | 归档时间: | 
 | 
| 查看次数: | 9417 次 | 
| 最近记录: |