2013-08-16 9 views
10

Mam procedurę przechowywaną InsertCars, która akceptuje listę zdefiniowanego przez użytkownika typu tabeli CarType.Zadzwoń do procedury składowanej z dappera, która akceptuje listę zdefiniowanego przez użytkownika typu tabeli

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 

Potrzebuję wywołać tę procedurę przechowywaną z Dappera. Przeszukałem go i znalazłem kilka rozwiązań.

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

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

ale pojawia się błąd:

Procedure or function InsertCars has too many arguments specified

procedury przechowywanej także InsertCars zwraca liczbę wstawionych wierszy; Potrzebuję uzyskać tę wartość.

Gdzie jest źródło problemu?

Mój problem polega również na tym, że mam samochody na ogólnej liście List<Car> Cars i chcę przekazać tę listę do procedury przechowywania. Istnieje elegancki sposób, jak to zrobić?

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

Dziękuję za pomoc

edytowany

znalazłem rozwiązań

Does Dapper support SQL 2008 Table-Valued Parameters?

lub

Does Dapper support SQL 2008 Table-Valued Parameters 2?

Więc spróbuj zrobić własnością głupich klasy pomocnika

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; 
    } 
} 

Korzystając

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

otrzymuję wyjątek

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 

Co jest nie tak?

FIXED:

połączeń con.Execute zamiast con.Query

Odpowiedz

6

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

Trzeba zamienić rodzajowe lista samochodów do DataTable a następnie przekazać je do przechowywana. Należy zauważyć, że kolejność pól musi być taka sama, jak zdefiniowano w tabeli zdefiniowanej przez użytkownika typ w bazie danych. W przeciwnym razie dane nie będą zapisywane poprawnie. I musi on mieć również tę samą liczbę kolumn, co.

Używam tej metody do zamiany listy na DataTable. Możesz to nazwać jak swojąListę.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; 
    } 
+0

pierwszy problem ist przejścia parametr do procedury sklepu. Myślę, że liczenie i porządek są słuszne. Testuję moją procedurę za pomocą MS SQL Studio i działa dobrze. Zdefiniowany przez użytkownika typ tabeli ma tylko dwie kolumny: CARID i CARNAME. W DynamicParamater ustawiam tylko te dwie kolumny. Więc nie rozumiem, gdzie może być problem? – imodin

+0

@Wobe wspomniałem o pełnym sposobie zrobienia tego. dla parametrów typu tabeli przekazuję datatable jako parametr, a nie parametr dyamiczny – Ehsan

0

Innym rozwiązaniem byłoby nazwać jak ten

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

var result = con.Query<dynamic>("InsertCars", param); 

usunięcia: nowy CarDynamicParam (CAR), CommandType: CommandType.StoredProcedure

pomocą parametru typu tabeli bezpośrednio, to zadziała.

Jeśli możesz użyć Datatable (.net core nie obsługuje go), to jest bardzo proste.

Utwórz DataTable -> Dodaj wymagane kolumny, aby dopasować je do typu tabeli -> Dodaj wymagane wiersze. Na koniec, po prostu wywołaj to używając tak eleganckiego jak ten.

var result = con.Query<dynamic>("InsertCars", new{paramFromStoredProcedure=yourDataTableInstance}, commandType: CommandType.StoredProcedure); 
+1

Co robisz w .Net Core? –

+1

Pierwsza część rozwiązania jest dla .Net Core –

2

Wiem, że to trochę stare, ale myślałem, że mimo to postawię, ponieważ starałem się, aby było to łatwiejsze. Mam nadzieję, że zrobiłeś z pakietem Nuget tworzę, który pozwoli na kodzie jak:

pakietu
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); 

Nuget: https://www.nuget.org/packages/Dapper.ParameterExtensions/0.2.0 jeszcze we wczesnym stadium, więc nie może działać ze wszystkim!

Proszę przeczytać README i nie krępuj się przyczynić na GitHub: https://github.com/RasicN/Dapper-Parameters

+0

Dzięki za NuGet! – optim1st

+0

Czy znasz jakąkolwiek alternatywę rdzenia .net? – FiringSquadWitness

+0

Nie zaktualizowałem tego, aby pracować z rdzeniem .net, uważałem, że jest to open source, więc zachęcamy do zastanowienia się nad jego dodaniem. Jeśli nic więcej, dodaj go jako problem do śledzenia, a ja w końcu zajrzę do jego aktualizacji. – JCisar