How to execute in Dapper a stored procedure with user-defined table type (UDT) parameter?

c# dapper sql-server stored-procedures

Question

My saved procedure is here.InsertCars which takes a list of custom table typesCarType .

CREATE TYPE dbo.CarType
AS TABLE
(
    CARID int null,
    CARNAME varchar(800) not null,
);

CREATE PROCEDURE dbo.InsertCars
    @Cars AS CarType READONLY
AS
-- RETURN COUNT OF INSERTED ROWS
END

I must use Dapper to invoke this stored procedure. I looked for answers on Google.

 var param = new DynamicParameters(new{CARID= 66, CARNAME= "Volvo"});

 var result = con.Query("InsertCars", param, commandType: CommandType.StoredProcedure);

But there's a problem:

Procedure or function InsertCars has too many arguments specified

Likewise, a stored procedureInsertCars yields the number of added rows; I must get this information.

Where is the problem's source?

Another issue I have is that I have automobiles on a generic list.List<Car> Cars Moreover, I want to give this list to the store method. There is an elegant method to do this.

public class Car
{
    public CarId { get; set; }
    public CarName { get; set; }
}

Regards for the support

EDITED

I discovered remedies

Does Dapper support table-valued parameters from SQL 2008?

or

Support for SQL 2008 Table-Valued Parameters 2 in Dapper?

So, I attempt to create my own idiotic helper class.

class CarDynamicParam : Dapper.SqlMapper.IDynamicParameters
{
    private Car car;

    public CarDynamicParam(Car car)
    {
        this.car = car;
    }

    public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
    {
        var sqlCommand = (SqlCommand)command;

        sqlCommand.CommandType = CommandType.StoredProcedure;

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

        Microsoft.SqlServer.Server.SqlMetaData[] tvpDefinition =
                                                                {

                                                                    new Microsoft.SqlServer.Server.SqlMetaData("CARID", SqlDbType.Int),
                                                                    new Microsoft.SqlServer.Server.SqlMetaData("CARNAME", SqlDbType.NVarChar, 100),
                                                                };

        var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvpDefinition);
        rec.SetInt32(0, car.CarId);
        rec.SetString(1, car.CarName);

        carList.Add(rec);

        var p = sqlCommand.Parameters.Add("Cars", SqlDbType.Structured);
        p.Direction = ParameterDirection.Input;
        p.TypeName = "CarType";
        p.Value = carList;
    }
}

Use

var result = con.Query("InsertCars", new CarDynamicParam(car), commandType: CommandType.StoredProcedure);

I get exemption

When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id.

StackTrace:

   at Dapper.SqlMapper.GetDynamicDeserializer(IDataRecord reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 1308
   at Dapper.SqlMapper.GetDeserializer(Type type, IDataReader reader, Int32 startBound, Int32 length, Boolean returnNullIfFirstMissing) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 1141
   at Dapper.SqlMapper.<QueryInternal>d__d`1.MoveNext() in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 819
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 770
   at Dapper.SqlMapper.Query(IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in c:\Dev\Dapper\Dapper\SqlMapper.cs:line 715

What is the issue?

FIXED:

Call con.Execute instead con.Query

1
18
5/23/2017 12:17:01 PM

Accepted Answer

My problem is also that I have cars in generic list List Cars and I want pass this list to stored procedure. It exist elegant way how to do it ?

It is necessary to Your generic list Car should now be a datatable. before passing it to the storedprocedure. It's important to keep in mind that your database's field order must be identical to what is specified in the user-defined table type. If not, data won't store correctly. Additionally, it same number of columns is required.

I convert lists to data tables using this technique. You may refer to it as yourList. ToDataTable()

public static DataTable ToDataTable<T>(this List<T> iList)
    {
        DataTable dataTable = new DataTable();
        PropertyDescriptorCollection propertyDescriptorCollection =
            TypeDescriptor.GetProperties(typeof(T));
        for (int i = 0; i < propertyDescriptorCollection.Count; i++)
        {
            PropertyDescriptor propertyDescriptor = propertyDescriptorCollection[i];
            Type type = propertyDescriptor.PropertyType;

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                type = Nullable.GetUnderlyingType(type);


            dataTable.Columns.Add(propertyDescriptor.Name, type);
        }
        object[] values = new object[propertyDescriptorCollection.Count];
        foreach (T iListItem in iList)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = propertyDescriptorCollection[i].GetValue(iListItem);
            }
            dataTable.Rows.Add(values);
        }
        return dataTable;
    }
16
2/26/2020 9:00:39 AM

Popular Answer

Although I tried to make this a bit simpler, I realize this is a little outdated, but I thought I would post on it anyhow. I think I've succeeded in doing so by developing a NuGet package that will enable code like:

public class CarType
{
  public int CARID { get; set; }
  public string CARNAME{ get; set; }
}

var cars = new List<CarType>{new CarType { CARID = 1, CARNAME = "Volvo"}};

var parameters = new DynamicParameters();
parameters.AddTable("@Cars", "CarType", cars)

 var result = con.Query("InsertCars", parameters, commandType: CommandType.StoredProcedure);

the https://www.nuget.org/packages/Dapper.ParameterExtensions/0.2.0It's still early on, so not everything could be compatible. NuGet package

Please see the README before contributing to GitHub at https://github.com/RasicN/Dapper-Parameters.



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow