Does Dapper support SQL 2008 Table-Valued Paramete

2020-06-16 04:32发布

问题:

I know that dapper can support TVF, but how do you send extra parameters along with TVF (without adding it to the IntDynamicParam class)? See the below example from Tests.cs, i have modified to add the extra parameter:

connection.Execute("CREATE TYPE int_list_type AS TABLE (n int NOT NULL PRIMARY KEY)");
connection.Execute("CREATE PROC get_ints @x int, @ints int_list_type READONLY AS select * from @ints");

I tried the following but got errors (No mapping exists from object type SqlMapper.Tests+IntDynamicParam to a known managed provider native type.):

var p = new DynamicParameters();
p.Add("x", 4);
p.Add("ints",new IntDynamicParam(new int[] { 1, 2, 3 }));

var nums = connection.Query<int>("get_ints", p).ToList();

Thank you for the reply Sam, but the question was a little different. I want to know how to pass in another variable along with the tuple. See the modified SP below:

CREATE TYPE int_tuple_list_type AS TABLE (n int NOT NULL PRIMARY KEY, n2 int)

CREATE PROC get_int_tuples 
  @someVar varchar(10),
  @ints int_tuple_list_type READONLY
AS select * from @ints

回答1:

There is very little magic about IDynamicParameters all you need to worry about is implementing AddParameters on the ready to run open IDbCommand.

Say you wanted a tuple of ints, you could implement the following:

CREATE TYPE int_tuple_list_type 
     AS TABLE (n int NOT NULL PRIMARY KEY, n2 int)
CREATE PROC get_int_tuples @ints 
     int_tuple_list_type READONLY AS select * from @ints

Followed by:

class TupleIntDynamicParam : Dapper.SqlMapper.IDynamicParameters
{
    IEnumerable<int> tuples;
    public IntDynamicParam(IEnumerable<Tuple<int,int>> tuples)
    {
        this.tuples= tuples;
    }

    public void AddParameters(IDbCommand command)
    {
        var sqlCommand = (SqlCommand)command;
        sqlCommand.CommandType = CommandType.StoredProcedure;

        List<Microsoft.SqlServer.Server.SqlDataRecord> number_list = 
           new List<Microsoft.SqlServer.Server.SqlDataRecord>();

        // Create an SqlMetaData object that describes our table type.
        Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { 
          new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int), 
          new Microsoft.SqlServer.Server.SqlMetaData("n2", SqlDbType.Int) };

        foreach (int n in tuples)
        {
            // Create a new record, using the metadata array above.
            Microsoft.SqlServer.Server.SqlDataRecord rec = 
                new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition);
            rec.SetInt32(0, n.Item1);
            rec.SetInt32(1, n.Item2);
            number_list.Add(rec);      // Add it to the list.
        }

        // Add the table parameter.
        var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured);
        p.Direction = ParameterDirection.Input;
        p.TypeName = "int_tuple_list_type";
        p.Value = number_list;

    }
}

Then you can pass in tuples with:

var nums = connection.Query("get_int_tuples", 
      new TupleIntDynamicParam (new Tuple<int,int>[] 
      { 
           Tuple.Create(1,2), Tuple.Create(2,3) 
      })).ToList();


回答2:

Here's a complete solution using the ICustomQueryParameter interface and some code I wrote (in the bottom of attached page) to let you send any IEnumerable to a storedprocedure using dapper, even when T has multiple properties:

http://code.google.com/p/dapper-dot-net/issues/detail?can=2&start=0&num=100&q=ICustomQueryParameter&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary&groupby=&sort=&id=69

Here's the code from the link:

  /// <summary>
    /// Send DataTable as dapper parameter
    /// </summary>
    public class DapperTableParameter : ICustomQueryParameter
    {
        protected DataTable _table = null;

        public DapperTableParameter(DataTable table)
        {
            _table = table;
        }

        public void AddParameter(System.Data.IDbCommand command, string name)
        {
            // This is SqlConnection specific
            ((SqlCommand)command).Parameters.Add("@" + name, SqlDbType.Structured).Value = _table;
        }
    }



public class DapperTVP<T> : DapperTableParameter
    {
        public DapperTVP(IEnumerable<T> list) : base(new System.Data.DataTable())
        {
            var t = typeof(T);
            var propertyByName = new Dictionary<string, PropertyInfo>();

            foreach (var p in t.GetProperties())
            {
                propertyByName.Add(p.Name, p);
                _table.Columns.Add(p.Name, p.PropertyType);
            }

            foreach (var i in list)
            {
                var row = _table.NewRow();
                foreach (var p in propertyByName)
                {
                    row[p.Key] = p.Value.GetValue(i, null);
                }

                _table.Rows.Add(row);
            }
        }
    }

You're still gonna have to merge ICustomQueryParameter like this: http://code.google.com/p/dapper-dot-net/issues/attachmentText?id=69&aid=690000000&name=SqlMapper.patch&token=wFLdWLM4LPamcAwcDaGqcITaAmg%3A1392913796708



回答3:

This is how I did using a generic class. This is not perfect yet. Will update this post as I make changes to it.

public class TableTypeDynamicParam<T> : Dapper.SqlMapper.IDynamicParameters
{
    IEnumerable<T> MyList;
    Dictionary<string, int> ordinals;
    Dictionary<Type, SqlDbType> typeMap;
    string ParameterName, TableTypeName;
    public TableTypeDynamicParam(IEnumerable<T> listValue, string parameterName, string tableTypeName)
    {
        this.MyList = listValue;
        this.ParameterName = parameterName;
        this.TableTypeName = tableTypeName;
        ordinals = new Dictionary<string, int>();
        SetTypeMap();
    }

    void SetTypeMap()
    {
        typeMap = new Dictionary<Type, SqlDbType>();
        typeMap[typeof(byte)] = SqlDbType.TinyInt;
        typeMap[typeof(sbyte)] = SqlDbType.TinyInt;
        typeMap[typeof(short)] = SqlDbType.SmallInt;
        typeMap[typeof(ushort)] = SqlDbType.SmallInt;
        typeMap[typeof(int)] = SqlDbType.Int;
        typeMap[typeof(uint)] = SqlDbType.Int;
        typeMap[typeof(long)] = SqlDbType.Int;
        typeMap[typeof(ulong)] = SqlDbType.BigInt;
        typeMap[typeof(float)] = SqlDbType.Float;
        typeMap[typeof(double)] = SqlDbType.Float;
        typeMap[typeof(decimal)] = SqlDbType.Decimal;
        typeMap[typeof(bool)] = SqlDbType.Bit;
        typeMap[typeof(string)] = SqlDbType.Text;
        typeMap[typeof(char)] = SqlDbType.Char;
        typeMap[typeof(Guid)] = SqlDbType.UniqueIdentifier;
        typeMap[typeof(DateTime)] = SqlDbType.DateTime;
        typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset;
        typeMap[typeof(byte[])] = SqlDbType.Binary;
        typeMap[typeof(byte?)] = SqlDbType.TinyInt;
        typeMap[typeof(sbyte?)] = SqlDbType.TinyInt;
        typeMap[typeof(short?)] = SqlDbType.SmallInt;
        typeMap[typeof(ushort?)] = SqlDbType.SmallInt;
        typeMap[typeof(int?)] = SqlDbType.Int;
        typeMap[typeof(uint?)] = SqlDbType.Int;
        typeMap[typeof(long?)] = SqlDbType.BigInt;
        typeMap[typeof(ulong?)] = SqlDbType.BigInt;
        typeMap[typeof(float?)] = SqlDbType.Float;
        typeMap[typeof(double?)] = SqlDbType.Float;
        typeMap[typeof(decimal?)] = SqlDbType.Decimal;
        typeMap[typeof(bool?)] = SqlDbType.Bit;
        typeMap[typeof(char?)] = SqlDbType.Char;
        typeMap[typeof(Guid?)] = SqlDbType.UniqueIdentifier;
        typeMap[typeof(DateTime?)] = SqlDbType.DateTime;
        typeMap[typeof(DateTimeOffset?)] = SqlDbType.DateTimeOffset;
        //  typeMap[typeof(System.Data.Linq.Binary)] = SqlDbType.Binary;
    }

    SqlDbType GetSqlDBType(Type t)
    {

        # region other tries

        /*  SqlParameter p1 = new SqlParameter();

             TypeConverter tc = TypeDescriptor.GetConverter(p1.DbType);
             if (tc.CanConvertFrom(theType))
                 p1.DbType = (DbType)tc.ConvertFrom(theType.Name);
             else
             {
                 //Try brute force
                 try
                 {
                     p1.DbType = (DbType)tc.ConvertFrom(theType.Name);
                 }
                 catch {} //Do Nothing
             }
             return p1.SqlDbType; 
             Microsoft.SqlServer.Server.SqlMetaData sm = Microsoft.SqlServer.Server.SqlMetaData.InferFromValue(new byte[] { 1, 2 }, "someName");
sm = Microsoft.SqlServer.Server.SqlMetaData.InferFromValue(2.3, "someName");
sm = Microsoft.SqlServer.Server.SqlMetaData.InferFromValue(11, "someName");
sm =     Microsoft.SqlServer.Server.SqlMetaData.InferFromValue(System.Web.HttpValidationStatus.Valid, "someName");
sm = Microsoft.SqlServer.Server.SqlMetaData.InferFromValue("hello", "someName");

Console.WriteLine(sm.SqlDbType);
Console.WriteLine(sm.TypeName);
Console.WriteLine(sm.MaxLength);

             */
        # endregion

        return typeMap[t];

    }

    void SetRecordValue(ref Microsoft.SqlServer.Server.SqlDataRecord rec, string propertyName, object value, Type propertyType)
    {
        switch (propertyType.ToString())
        {
            //need to cover all type case
            case "System.Int32":
                rec.SetInt32(ordinals[propertyName], (int)value);
                break;
            case "System.Int64":
                rec.SetInt64(ordinals[propertyName], (long)value);
                break;
            case "System.Boolean":
                rec.SetBoolean(ordinals[propertyName], (bool)value);
                break;
            case "System.String":
            default:
                rec.SetString(ordinals[propertyName], Convert.ToString(value));
                break;
        }
    }

    public void AddParameters(IDbCommand command)
    {
        var sqlCommand = (SqlCommand)command;
        sqlCommand.CommandType = CommandType.StoredProcedure;

        List<Microsoft.SqlServer.Server.SqlDataRecord> tableType_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>();
        var first = MyList.FirstOrDefault();
        if (null != first)
        {
            var lstDefinition = new List<Microsoft.SqlServer.Server.SqlMetaData>();
            int i = 0;
            foreach (var prop in first.GetType().GetProperties())
            {
                lstDefinition.Add(new Microsoft.SqlServer.Server.SqlMetaData(prop.Name, GetSqlDBType(prop.PropertyType)));
                ordinals.Add(prop.Name, i++);
            }
            // Create an SqlMetaData object that describes our table type.
            // Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) };

            foreach (var l in MyList)
            {
                // Create a new record, using the metadata array above.
                Microsoft.SqlServer.Server.SqlDataRecord rec = new Microsoft.SqlServer.Server.SqlDataRecord(lstDefinition.ToArray());
                foreach (var prop in first.GetType().GetProperties())
                {

                    // rec.SetInt32(ordinals[prop.Name], prop.GetValue(l));    // Set the value.
                    SetRecordValue(ref rec, prop.Name, prop.GetValue(l), prop.GetType());
                }
                tableType_list.Add(rec);      // Add it to the list.
            }
        }

        // Add the table parameter.
        var p = sqlCommand.Parameters.Add("@" + ParameterName.TrimStart('@'), SqlDbType.Structured);
        p.Direction = ParameterDirection.Input;
        p.TypeName = TableTypeName;
        if (null != first)
            p.Value = tableType_list;
        else
            p.Value = DBNull.Value;

    }

    public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
    {
        AddParameters(command);
    }
}


回答4:

There is a nuget package, Dapper TVP, that includes some classes that make using table valued parameters easier.

It should be noted that the package is dependent upon Dapper 1.12.1 or higher.

Using this package the passing a tvp to a stored procedure looks something like this:

        var p = new Dapper.Tvp.DynamicParametersTvp();

        SqlMetaData[] tableValuedParameterDefinition = 
        { 
            new SqlMetaData("columnOne", SqlDbType.UniqueIdentifier), 
            new SqlMetaData("columnTwo", SqlDbType.VarBinary, 64)
        };

        var collectionRows = new List<SqlDataRecord>();

        for (int i = 0; i < collection.Count; i++)
        {
            var row = new SqlDataRecord(tableValuedParameterDefinition);
            row.SetGuid(0, collection[i].Item1);
            row.SetBytes(1, 0, collection[i].Item2, 0, sessionsAndPins[i].Item2.Length);
            collectionRows.Add(row);
        }

        p.Add(new Dapper.Tvp.TableValueParameter("@tvpName", "tvpTypeName", collectionRows));

        using (SqlConnection connection = new SqlConnection(ConnectionManager.GetConnectionString("Database")))
        {
            try
            {
                connection.Open();

                result = connection.Query<SomeObject>("SomeStoredProcedureThatExpectsTVP", p, commandType: CommandType.StoredProcedure).SingleOrDefault();
            }
            finally
            {
                connection.Close();
            }
        }


回答5:

I needed to be able to pass table-valued parameters and regular parameters too. Combining @Esen and @Roger-Joys answers gave me the below class. This class is specifically targeted to use the stringlist_to_table type, but you could change it to suit your needs. It seemed to make sense to me to have a specific method for each TVP type that you would deal with.

CREATE TYPE stringlist_tbltype AS TABLE (s VARCHAR(256))

Usage:

var p = new DynamicParametersWithTVP();
p.Add( "@someVar", "abc", DbType.AnsiString );
p.AddTVPString( "@stringlist", new string[] { "abc", "def" } );

using (var conn = new SqlConnection( _cs )) {
    conn.Open();
    return conn.Query( "dbo.stored_proc_name", p, commandType: CommandType.StoredProcedure );
}

Here is the implementation of DynamicParametersWithTVP. It basically keeps a DynamicParameters class around to handle the non-TVP stuff, and the builds off of that for TVP.

class DynamicParametersWithTVP : SqlMapper.IDynamicParameters
{
    private Dictionary<string, TVPData> _tvpParams;
    private DynamicParameters _params;

    public DynamicParametersWithTVP()
    {
        _tvpParams = new Dictionary<string, TVPData>();
        _params = new DynamicParameters();
    }

    public void Add( string name, object value = null, DbType? dbType = null, ParameterDirection? direction = null, int? size = null )
    {
        _params.Add( name, value, dbType, direction, size );
    }

    public void AddTVPString( string name, IEnumerable<string> values )
    {
        var metadata = new[] {
            new SqlMetaData( "one", SqlDbType.VarChar, 256)
        };

        var data = new TVPData
        {
            Data = values,
            MetaData = metadata,
            Name = name,
            Type = "stringlist_tbltype",
            SetData = ( rec, s ) => rec.SetString( 0, (string)s ),
        };

        _tvpParams.Add( name, data );
    }

    public void AddParameters( IDbCommand command, SqlMapper.Identity identity )
    {
        //Adds the regular parameters, then adds any tvp parameters
        ((SqlMapper.IDynamicParameters)_params).AddParameters( command, identity );
        AddTVPCommands( command );
    }

    private void AddTVPCommands( IDbCommand command )
    {
        foreach (var o in _tvpParams) {
            AddTVPParam( command, o.Key, o.Value );
        }
    }

    private void AddTVPParam( IDbCommand command, string name, TVPData value )
    {
        var dataList = new List<SqlDataRecord>();
        if (value.Data != null) {
            foreach (var d in value.Data) {
                var rec = new SqlDataRecord( value.MetaData );
                value.SetData( rec, d ); //note: this only works for a one-column TVP
                dataList.Add( rec );
            }
        }
        if (dataList.Count == 0) { //gotta make the value null if there isn't any data.
            dataList = null;
        }

        var p = ((SqlCommand)command).Parameters.Add( name, SqlDbType.Structured );
        p.Direction = ParameterDirection.Input;
        p.TypeName = value.Type;
        p.Value = dataList;
    }

    private class TVPData
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public SqlMetaData[] MetaData { get; set; }
        public IEnumerable Data { get; set; }
        public Action<SqlDataRecord, object> SetData { get; set; }
    }
}


回答6:

I prefer to do this:

internal class TableTypeDynamicParam : SqlMapper.IDynamicParameters {
    protected readonly List<object> Lst = new List<object>();
    protected SqlCommand Cmd;
    public void Add(DataTable dt) {
        if(dt != null)
            Lst.Add(dt);
    }

    public void Add(DataSet ds) {
        if(ds != null)
            foreach(DataTable dt in ds.Tables) {
                Lst.Add(dt);
            }
    }

    public void Add(string name, object value = null, SqlDbType? dbType = null, ParameterDirection direction = ParameterDirection.Input, int? size = null) {
        var par = value != null ? new SqlParameter(name, value) : new SqlParameter(name, dbType);
        par.Direction = direction;
        if(size.HasValue)
            par.Size = size.Value;
        Lst.Add(par);
    }

    public void Add(SqlParameter par) {
        Lst.Add(par);
    }

    public void AddRange(IEnumerable<SqlParameter> pars) {
        if(pars != null)
            Lst.AddRange(pars);
    }

    public void AddParameters(IDbCommand command) {
        Cmd = (SqlCommand)command;

        foreach(var o in Lst) {
            var dt = o as DataTable;
            if(dt != null) {
                var parameter = Cmd.Parameters.AddWithValue(dt.ExtendedProperties["parameterName"].ToString(), dt);
                parameter.SqlDbType = SqlDbType.Structured;
                parameter.TypeName = dt.TableName;
            } else {
                var par = o as SqlParameter;
                if(par != null)
                    Cmd.Parameters.Add(par);
            }
        }
    }

    public void AddParameters(IDbCommand command, SqlMapper.Identity identity) {
        AddParameters(command);
    }

    public T Get<T>(string name) {
        return (T)Cmd.Parameters[name].Value;
    }

    public ERPDictionay GetLst(ERPLst<SqlParameter> pars) {
        var dic = new ERPDictionay();
        if(pars != null) {
            dic.QtdItemsPageParameterName = pars.QtdItemsPageParameterName;
            dic.QtdItemsPage = pars.QtdItemsPage;
            dic.CurrentPageParameterName = pars.CurrentPageParameterName;
            dic.CurrentPage = pars.CurrentPage;
        };
        for(var i = 0; i < Cmd.Parameters.Count; i++) {
            var a = Cmd.Parameters[i];
            if (a.Direction != ParameterDirection.InputOutput && a.Direction != ParameterDirection.Output) continue;
            if(pars != null && a.ParameterName == pars.TotalItensDBParameterName) {
                dic.TotalItensDB = Convert.ToInt32(Cmd.Parameters[i].Value);
            } else {
                dic.Add(a.ParameterName, Cmd.Parameters[i].Value);
            }
        }
        return dic;
    }
}

internal class TableTypeDynamicParam<T> : TableTypeDynamicParam
{
    private ERPLstInput<T> _lstItems;

    public void Add(ERPLstInput<T> lstItems)
    {
        if (lstItems != null)
            _lstItems = lstItems;
    }

    private static Dictionary<Type, SqlDbType> _typeMap;

    private static Dictionary<Type, SqlDbType> TypeMap
    {
        get
        {
            if (_typeMap != null)
                return _typeMap;
            _typeMap = new Dictionary<Type, SqlDbType>();
            _typeMap[typeof(byte)] = SqlDbType.TinyInt;
            _typeMap[typeof(sbyte)] = SqlDbType.TinyInt;
            _typeMap[typeof(short)] = SqlDbType.SmallInt;
            _typeMap[typeof(ushort)] = SqlDbType.SmallInt;
            _typeMap[typeof(int)] = SqlDbType.Int;
            _typeMap[typeof(uint)] = SqlDbType.Int;
            _typeMap[typeof(long)] = SqlDbType.Int;
            _typeMap[typeof(ulong)] = SqlDbType.BigInt;
            _typeMap[typeof(float)] = SqlDbType.Float;
            _typeMap[typeof(double)] = SqlDbType.Float;
            _typeMap[typeof(decimal)] = SqlDbType.Decimal;
            _typeMap[typeof(bool)] = SqlDbType.Bit;
            _typeMap[typeof(string)] = SqlDbType.Text;
            _typeMap[typeof(char)] = SqlDbType.Char;
            _typeMap[typeof(Guid)] = SqlDbType.UniqueIdentifier;
            _typeMap[typeof(DateTime)] = SqlDbType.DateTime;
            _typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset;
            _typeMap[typeof(byte[])] = SqlDbType.Binary;
            _typeMap[typeof(byte?)] = SqlDbType.TinyInt;
            _typeMap[typeof(sbyte?)] = SqlDbType.TinyInt;
            _typeMap[typeof(short?)] = SqlDbType.SmallInt;
            _typeMap[typeof(ushort?)] = SqlDbType.SmallInt;
            _typeMap[typeof(int?)] = SqlDbType.Int;
            _typeMap[typeof(uint?)] = SqlDbType.Int;
            _typeMap[typeof(long?)] = SqlDbType.BigInt;
            _typeMap[typeof(ulong?)] = SqlDbType.BigInt;
            _typeMap[typeof(float?)] = SqlDbType.Float;
            _typeMap[typeof(double?)] = SqlDbType.Float;
            _typeMap[typeof(decimal?)] = SqlDbType.Decimal;
            _typeMap[typeof(bool?)] = SqlDbType.Bit;
            _typeMap[typeof(char?)] = SqlDbType.Char;
            _typeMap[typeof(Guid?)] = SqlDbType.UniqueIdentifier;
            _typeMap[typeof(DateTime?)] = SqlDbType.DateTime;
            _typeMap[typeof(DateTimeOffset?)] = SqlDbType.DateTimeOffset;
            return _typeMap;
        }
    }

    static SqlDbType GetSqlDBType(Type t) {
        return TypeMap[t];
    }

    void SetRecordValue(ref SqlDataRecord rec, string propertyName, object value, Type propertyType) {
        switch(propertyType.ToString()) {
            case "System.Int32":
                rec.SetInt32(def.IndexOf(a=>a.Name == propertyName), (int)value);
                break;
            case "System.Int64":
                rec.SetInt64(def.IndexOf(a => a.Name == propertyName), (long)value);
                break;
            case "System.Boolean":
                rec.SetBoolean(def.IndexOf(a => a.Name == propertyName), (bool)value);
                break;
            case "System.String":
            default:
                rec.SetString(def.IndexOf(a => a.Name == propertyName), Convert.ToString(value));
                break;
        }
    }

    private List<SqlMetaData> def;
    public new void AddParameters(IDbCommand command) {
        Cmd = (SqlCommand)command;

        foreach(var o in Lst) {
            var dt = o as DataTable;
            if(dt != null) {
                var parameter = Cmd.Parameters.AddWithValue(dt.ExtendedProperties["parameterName"].ToString(), dt);
                parameter.SqlDbType = SqlDbType.Structured;
                parameter.TypeName = dt.TableName;
            } else {
                var par = o as SqlParameter;
                if(par != null)
                    Cmd.Parameters.Add(par);
            }
        }

        if (_lstItems != null)
        {
            var tableTypeList = new List<SqlDataRecord>();
            var first = _lstItems.FirstOrDefault();
            if (first != null)
            {
                def = new List<SqlMetaData>();
                int i = 0;
                foreach (var prop in first.GetType().GetProperties())
                {
                    def.Add(new SqlMetaData(prop.Name, GetSqlDBType(prop.PropertyType)));
                }
                foreach (var l in _lstItems)
                {
                    var rec = new SqlDataRecord(def.ToArray());
                    foreach (var prop in first.GetType().GetProperties())
                    {
                        SetRecordValue(ref rec, prop.Name, prop.GetValue(l), prop.GetType());
                    }
                    tableTypeList.Add(rec);
                }
            }

            var p = Cmd.Parameters.Add("@" + _lstItems.ParameterName.TrimStart('@'), SqlDbType.Structured);
            p.Direction = ParameterDirection.Input;
            p.TypeName = _lstItems.TableTypeName;
            if (null != first)
                p.Value = tableTypeList;
            else
                p.Value = DBNull.Value;
        }
    }
}

The dictionary is this:

[Serializable]
public class ERPDictionay : Dictionary<string,object> {

    private string _currentPageParameterName = "@currentPage";
    public string CurrentPageParameterName {
        get { return _currentPageParameterName; }
        set { _currentPageParameterName = value; }
    }

    private string _qtdItemsPageParameterName = "@qtdItemsPage";
    public string QtdItemsPageParameterName {
        get { return _qtdItemsPageParameterName; }
        set { _qtdItemsPageParameterName = value; }
    }

    private string _totalItensDBParameterName = "@totalItensDB";
    public string TotalItensDBParameterName {
        get { return _totalItensDBParameterName; }
        set { _totalItensDBParameterName = value; }
    }

    public int CurrentPage { get; set; }
    public int QtdItemsPage { get; set; }
    public int TotalItensDB { get; set; }

    public ERPDictionay() { }

    public ERPDictionay(
        int qtdItemsPage,
        int currentPage = 0,
        string currentPageParameterName = null,
        string qtdItemsPageParameterName = null,
        string totalItensDBParameterName = null)
    {
        QtdItemsPage = qtdItemsPage;
        CurrentPage = currentPage;
        if (!string.IsNullOrEmpty(currentPageParameterName))
            _currentPageParameterName = currentPageParameterName;
        if(!string.IsNullOrEmpty(qtdItemsPageParameterName))
            _qtdItemsPageParameterName = qtdItemsPageParameterName;
        if(!string.IsNullOrEmpty(totalItensDBParameterName))
            _totalItensDBParameterName = totalItensDBParameterName;
    }
}


回答7:

If you derive your custom class off DynamicParameters and implement IDynamicParameters, you can solve the issue stated above.

public class DynamicParametersWithTVP : Dapper.DynamicParameters, Dapper.SqlMapper.IDynamicParameters
{

.....

I wrote a general purpose class that lets you call a proc like this:

        var procParams = new DynamicParametersWithTVP();
        procParams.Add("@Param1", someValue);
        procParams.Add("@Param2", someOtherValue);
        procParams.AddTVP("@TableParam1", "dbo.someTableType", someEnumberableListOfObject);