Dapper.NET
Parametro Sintassi di riferimento
Ricerca…
Parametri
Parametro | Dettagli |
---|---|
this cnn | La connessione al database sottostante - this denota un metodo di estensione; la connessione non ha bisogno di essere aperta - se non è aperta, viene aperta e chiusa automaticamente. |
<T> / Type | (facoltativo) il tipo di oggetto da restituire; se viene utilizzata l'API non generica / non di Type , viene restituito un oggetto dynamic per riga, simulando una proprietà denominata per nome di colonna restituita dalla query (questo oggetto dynamic implementa anche IDicionary<string,object> ). |
sql | L'SQL da eseguire |
param | (facoltativo) I parametri da includere. |
transaction | (facoltativo) La transazione del database da associare al comando |
buffered | (facoltativo) Se pre-consumare i dati in un elenco (predefinito), anziché esporre un IEnumerable aperto sul lettore live |
commandTimeout | (facoltativo) il timeout da utilizzare sul comando; se non specificato, si presume SqlMapper.Settings.CommandTimeout (se specificato) |
commandType | Il tipo di comando che viene eseguito; il valore predefinito è CommandText |
Osservazioni
La sintassi per esprimere i parametri varia tra RDBMS. Tutti gli esempi sopra riportati utilizzano la sintassi di SQL Server, ovvero @foo
; tuttavia, ?foo
e :foo
dovrebbero funzionare bene.
SQL parametrico di base
Dapper semplifica il seguire le migliori pratiche mediante SQL completamente parametrizzato.
I parametri sono importanti, quindi dapper rende facile farlo bene. Devi semplicemente esprimere i tuoi parametri nel modo normale per il tuo RDBMS (solitamente @foo
?foo
o :foo
) e dare a dapper un oggetto che ha un membro chiamato foo
. Il modo più comune per farlo è con un tipo anonimo:
int id = 123;
string name = "abc";
connection.Execute("insert [KeyLookup](Id, Name) values(@id, @name)",
new { id, name });
E ... questo è tutto. Dapper aggiungerà i parametri richiesti e tutto dovrebbe funzionare.
Usando il tuo modello a oggetti
Puoi anche usare il tuo modello di oggetto esistente come parametro:
KeyLookup lookup = ... // some existing instance
connection.Execute("insert [KeyLookup](Id, Name) values(@Id, @Name)", lookup);
Dapper usa il comando-testo per determinare quali membri dell'oggetto aggiungere - di solito non aggiunge cose non necessarie come Description
, IsActive
, CreationDate
perché il comando che abbiamo emesso chiaramente non li coinvolge - anche se ci sono casi in cui potrebbe farlo, per esempio se il tuo comando contiene:
// TODO - removed for now; include the @Description in the insert
Non tenta di capire che quanto sopra è solo un commento.
Procedura di archiviazione
I parametri delle stored procedure funzionano esattamente allo stesso modo, ad eccezione del fatto che dapper non può tentare di determinare cosa dovrebbe / non dovrebbe essere incluso - tutto ciò che è disponibile viene trattato come un parametro. Per questo motivo, i tipi anonimi sono generalmente preferiti:
connection.Execute("KeyLookupInsert", new { id, name },
commandType: CommandType.StoredProcedure);
Valore Inline
A volte la convenienza di un parametro (in termini di manutenzione ed espressività) può essere superata dal suo costo in termini di prestazioni per trattarlo come un parametro. Ad esempio, quando la dimensione della pagina è fissata da un'impostazione di configurazione. O un valore di stato è abbinato a un valore enum
. Tenere conto:
var orders = connection.Query<Order>(@"
select top (@count) * -- these brackets are an oddity of SQL Server
from Orders
where CustomerId = @customerId
and Status = @open", new { customerId, count = PageSize, open = OrderStatus.Open });
L'unico parametro reale qui è customerId
- gli altri due sono pseudo-parametri che in realtà non cambieranno. Spesso il RDBMS può fare un lavoro migliore se rileva questi come costanti. Dapper ha una sintassi speciale per questo - {=name}
invece di @name
- che si applica solo ai tipi numerici. (Ciò minimizza qualsiasi superficie di attacco dall'iniezione SQL). Un esempio è il seguente:
var orders = connection.Query<Order>(@"
select top {=count} *
from Orders
where CustomerId = @customerId
and Status = {=open}", new { customerId, count = PageSize, open = OrderStatus.Open });
Dapper sostituisce i valori con i letterali prima di emettere l'SQL, quindi l'RDBMS vede effettivamente qualcosa del tipo:
select top 10 *
from Orders
where CustomerId = @customerId
and Status = 3
Ciò è particolarmente utile quando si consente ai sistemi RDBMS non solo di prendere decisioni migliori, ma di aprire piani di query che i parametri attuali impediscono. Ad esempio, se un predicato di colonna è contrario a un parametro, non è possibile utilizzare un indice filtrato con valori specifici su tali colonne. Questo perché la prossima query potrebbe avere un parametro diverso da uno di quei valori specificati.
Con valori letterali, Query Optimizer è in grado di utilizzare gli indici filtrati poiché sa che il valore non può cambiare nelle query future.
Elenca espansioni
Uno scenario comune nelle query di database è IN (...)
dove l'elenco qui viene generato in fase di esecuzione. La maggior parte degli RDBMS non ha una buona metafora per questo - e per questo non esiste una soluzione cross-RDBMS universale. Invece, dapper offre una leggera espansione automatica dei comandi. Tutto ciò che è richiesto è un valore di parametro fornito che è IEnumerable
. Un comando che coinvolge @foo
viene esteso a (@foo0,@foo1,@foo2,@foo3)
(per una sequenza di 4 elementi). L'uso più comune di questo sarebbe IN
:
int[] orderIds = ...
var orders = connection.Query<Order>(@"
select *
from Orders
where Id in @orderIds", new { orderIds });
Questo quindi si espande automaticamente per emettere il codice SQL appropriato per il recupero di più righe:
select *
from Orders
where Id in (@orderIds0, @orderIds1, @orderIds2, @orderIds3)
con i parametri @orderIds0
ecc vengono aggiunti come valori presi da arrray. Si noti che il fatto che SQL non sia originariamente valido è intenzionale, per garantire che questa funzione non venga utilizzata per errore. Questa funzionalità funziona anche correttamente con OPTIMIZE FOR
/ UNKNOWN
query-hint in SQL Server; se usi:
option (optimize for
(@orderIds unknown))
lo espanderà correttamente per:
option (optimize for
(@orderIds0 unknown, @orderIds1 unknown, @orderIds2 unknown, @orderIds3 unknown))
Esecuzione di operazioni su più insiemi di input
A volte, vuoi fare la stessa cosa più volte. Dapper supporta questo metodo nel metodo Execute
se il parametro più esterno (che di solito è un singolo tipo anonimo o un'istanza del modello di dominio) viene effettivamente fornito come una sequenza IEnumerable
. Per esempio:
Order[] orders = ...
// update the totals
connection.Execute("update Orders set Total=@Total where Id=@Id", orders);
Qui, dapper sta facendo un semplice ciclo sui nostri dati, essenzialmente come se avessimo fatto:
Order[] orders = ...
// update the totals
foreach(Order order in orders) {
connection.Execute("update Orders set Total=@Total where Id=@Id", order);
}
Questo utilizzo diventa particolarmente interessante quando combinato con l'API async
su una connessione che è esplicitamente configurata per tutti i "Multiple Active Result Set" - in questo utilizzo, dapper eseguirà automaticamente la pipeline delle operazioni, quindi non si paga il costo di latenza per riga. Ciò richiede un utilizzo leggermente più complicato,
await connection.ExecuteAsync(
new CommandDefinition(
"update Orders set Total=@Total where Id=@Id",
orders, flags: CommandFlags.Pipelined))
Si noti, tuttavia, che si potrebbe anche voler esaminare i parametri valutati in tabella.
Parametri pseudo-posizionali (per provider che non supportano parametri denominati)
Alcuni provider ADO.NET (in particolare: OleDB) non supportano i parametri denominati ; i parametri sono invece specificati solo dalla posizione , con il ?
posizionare titolare. Dapper non saprebbe quale membro usare per questi, quindi dapper permette una sintassi alternativa,? ?foo?
; questo sarebbe lo stesso di @foo
o :foo
in altre varianti SQL, ad eccezione del fatto che dapper sostituirà completamente il token dei parametri con ?
prima di eseguire la query.
Funziona in combinazione con altre funzionalità come l'espansione delle liste, quindi quanto segue è valido:
string region = "North";
int[] users = ...
var docs = conn.Query<Document>(@"
select * from Documents
where Region = ?region?
and OwnerId in ?users?", new { region, users }).AsList();
I membri .region
e .users
vengono utilizzati di conseguenza e l'SQL rilasciato è (ad esempio, con 3 utenti):
select * from Documents
where Region = ?
and OwnerId in (?,?,?)
Si noti, tuttavia, che dapper non consente di utilizzare lo stesso parametro più volte quando si utilizza questa funzione; questo per evitare di dover aggiungere lo stesso valore del parametro (che potrebbe essere grande) più volte. Se è necessario fare riferimento allo stesso valore più volte, prendere in considerazione la dichiarazione di una variabile, ad esempio:
declare @id int = ?id?; // now we can use @id multiple times in the SQL
Se le variabili non sono disponibili, puoi utilizzare nomi di membri duplicati nei parametri - questo renderà anche ovvio che il valore viene inviato più volte:
int id = 42;
connection.Execute("... where ParentId = $id0$ ... SomethingElse = $id1$ ...",
new { id0 = id, id1 = id });