У меня проблема с выполнением существующей хранимой процедуры с помощью Dapper. Хранимая процедура принимает некоторые параметры и список объектов определяемого пользователем типа. Я имитировал необходимые параметры в C#, но некоторые преобразования проходят некорректно, что вызывает исключение (перечисленное ниже), и я не могу понять, как его решить.
Хранимая процедура SQL Server:
ALTER PROCEDURE [dbo].[MySp]
@Name VARCHAR(60),
@Size BIGINT,
@Photos MyPhoto READONLY
AS
BEGIN
-- ... logic
где MyPhoto
— определяемый пользователем тип таблицы с:
Name VARCHAR(25),
Model VARCHAR(25)
На стороне C# у меня есть:
public class MyClassForSP
{
public string Name { get; set; }
public Int64 Size { get; set; }
public List<Photos> Photos { get; set; }
}
public class Photos
{
public string Name { get; set }
public string Model { get; set; }
}
Пока что для меня все выглядит логично... поэтому я попытался выполнить SQL-запрос:
public void ExecuteSp(MyClassForSP classForSp)
{
using IDbConnection connection = new SqlConnection(_settings.ConnectionString);
var result = await connection.QuerySingleAsync<MyResult>("dbo.MySp", classForSp,
commandType: CommandType.StoredProcedure);
}
В результате я получаю ошибку Dapper:
System.NotSupportedException: «Член типа MyNameSpace.Photos нельзя использовать в качестве значения параметра»
Вам необходимо использовать табличный параметр, указав DataTable
.
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Model");
foreach (var photo in classForSp.Photos)
{
dt.Rows.Add(photo.Name, photo.Model);
}
using IDbConnection connection = new SqlConnection(_settings.ConnectionString);
var result = await connection.QuerySingleAsync<MyResult>("dbo.MySp",
new
{
Name = classForSp.Name,
Size = classForSp.Size,
Photos = dt.AsTableValuedParameter("MyPhoto")
},
commandType: CommandType.StoredProcedure);
Если вы ищете общий способ преобразования списка в DataTable
для параметра с табличным значением, вы можете реализовать метод расширения, как показано ниже:
public static class IListExtensions
{
public static DataTable ToDataTable<T>(this IList<T> list)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable dataTable = new DataTable();
foreach (PropertyDescriptor property in properties)
dataTable.Columns.Add(property.Name, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType);
foreach (T item in list)
{
DataRow row = dataTable.NewRow();
foreach (PropertyDescriptor property in properties)
row[property.Name] = property.GetValue(item) ?? DBNull.Value;
dataTable.Rows.Add(row);
}
return dataTable;
}
}
Звонивший:
DataTable dt = classForSp.Photos.ToDataTable();
Это работает, но есть ли у нас более удобные способы преобразования класса данных в параметры без дополнительного ввода тех же имен параметров?
Привет, насколько я понимаю, вы хотите преобразовать класс данных в DataTable общим способом? Если да, я добавляю свой ответ о написании метода расширения для преобразования в DataTable. Демо
Это может помочь — Параметр с табличным значением Dapper. По сути, вы не можете передать перечисляемое напрямую и ожидать неявного перевода в свой TVP. Сначала вам нужно создать таблицу данных в правильном формате, а затем передать ее в качестве параметра, используя
DataTable.AsTableValuedParameter()