2013-08-16 1 views
10

Ho una procedura memorizzata InsertCars che accetta l'elenco del tipo di tabella definito dall'utente CarType.Procedura memorizzata chiamata da dapper che accetta l'elenco del tipo di tabella definito dall'utente

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 

Ho bisogno di chiamare questa stored procedure da Dapper. L'ho cercato su google e ho trovato alcune soluzioni.

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

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

ma ottengo un errore:

Procedure or function InsertCars has too many arguments specified

memorizzata anche procedure InsertCars restituisce il numero di righe inserite; Ho bisogno di ottenere questo valore.

Dove si trova la radice del problema?

Il mio problema è anche che ho le macchine nella lista generica List<Car> Cars e voglio passare questa lista per memorizzare la procedura. Esiste un modo elegante come farlo?

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

Grazie per l'aiuto

CURA

ho trovato soluzioni

Does Dapper support SQL 2008 Table-Valued Parameters?

o

Does Dapper support SQL 2008 Table-Valued Parameters 2?

quindi cerco fare propria classe di supporto stupido

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

Usa

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

ottengo un'eccezione

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 

Cosa c'è che non va?

FISSO:

chiamata con.Execute invece con.Query

risposta

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 ?

È necessario convertire il vostro elenco generico auto ad un DataTable e poi passarlo a StoredProcedure. Un punto da notare è che l'ordine dei campi deve essere lo uguale a quello definito nel tipo di tabella definito dall'utente nel database. Altrimenti i dati non verranno salvati correttamente. E lo deve avere lo stesso numero di colonne.

Io uso questo metodo per convertire List in DataTable. Puoi chiamarlo come tua lista.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

Primo parametro passaggio problema ist a procedura di memorizzazione. Penso che il conteggio e l'ordine siano giusti. Metto alla prova la mia procedura tramite MS SQL Studio e funziona bene. Il tipo di tabella definito dall'utente ha solo 2 colonne, CARID e CARNAME. In DynamicParamater ho impostato solo queste due colonne. Quindi non capisco dove può essere un problema? – imodin

+0

@Wobe ho menzionato il modo completo di farlo. per i parametri del tipo di tabella passo datatable come parametro e non come parametro dinamometrico – Ehsan

0

L'altra soluzione sarebbe quella di chiamare in questo modo

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

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

Togliere: nuova CarDynamicParam (auto), CommandType: CommandType.StoredProcedure

Utilizzare il parametro di tipo di tabella direttamente, funzionerà.

Se è possibile utilizzare Datatable (.net core non lo supporta), è molto semplice.

Crea DataTable -> Aggiungi le colonne richieste per abbinarle al tipo di tabella -> Aggiungi righe richieste. Finalmente basta chiamarlo usando dapper come questo.

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

Cosa fai in .Net Core allora? –

+1

La prima parte della soluzione è per .Net Core –

2

So che questo è un po 'vecchio, ma ho pensato di postare su questo comunque dato ho cercato di fare di questo un po' più facile. Spero di aver fatto con un pacchetto di NuGet creo che permetterà il codice come:

pacchetto
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 ancora nelle sue fasi iniziali, quindi non può funzionare con tutto!

Si prega di leggere il file README e sentitevi liberi di contribuire su GitHub: https://github.com/RasicN/Dapper-Parameters

+0

Grazie per NuGet! – optim1st

+0

conosci qualche alternativa .net core? – FiringSquadWitness

+0

Non l'ho aggiornato per funzionare con .net core pensava che fosse open source, quindi sentitevi liberi di cercare di aggiungerlo. Se non altro, per favore aggiungilo come problema per rintracciarlo e alla fine cercherò di aggiornarlo. – JCisar