Dapper.NET
Referencia de sintaxis de parámetros
Buscar..
Parámetros
Parámetro | Detalles |
---|---|
this cnn | La conexión de base de datos subyacente - this denota un método de extensión; la conexión no necesita estar abierta; si no está abierta, se abre y se cierra automáticamente. |
<T> / Type | (opcional) El tipo de objeto a devolver; si se usa la API no genérica / no Type , se devuelve un objeto dynamic por fila, simulando una propiedad nombrada por el nombre de columna devuelto por la consulta (este objeto dynamic también implementa IDicionary<string,object> ). |
sql | El SQL para ejecutar |
param | (Opcional) Los parámetros a incluir. |
transaction | (Opcional) La transacción de la base de datos para asociar con el comando. |
buffered | (opcional) si se debe consumir previamente los datos en una lista (el valor predeterminado), en lugar de exponer un IEnumerable abierto sobre el lector en vivo |
commandTimeout | (opcional) El tiempo de espera para usar en el comando; si no se especifica, se asume SqlMapper.Settings.CommandTimeout (si se especifica) |
commandType | El tipo de comando que se está ejecutando; por defecto a CommandText |
Observaciones
La sintaxis para expresar parámetros varía entre RDBMS. Todos los ejemplos anteriores utilizan la sintaxis de SQL Server, es decir, @foo
; sin embargo ?foo
y :foo
también deberían funcionar bien.
SQL básico parametrizado
Dapper facilita el seguimiento de las mejores prácticas mediante SQL totalmente parametrizado.
Los parámetros son importantes, por lo que Dapper hace que sea fácil hacerlo bien. Simplemente expresa tus parámetros de la forma normal para tu RDBMS (generalmente @foo
?foo
o :foo
) y le das a Dapper un objeto que tiene un miembro llamado foo
. La forma más común de hacer esto es con un tipo anónimo:
int id = 123;
string name = "abc";
connection.Execute("insert [KeyLookup](Id, Name) values(@id, @name)",
new { id, name });
Y eso es. Dapper agregará los parámetros requeridos y todo debería funcionar.
Usando tu modelo de objeto
También puede usar su modelo de objeto existente como parámetro:
KeyLookup lookup = ... // some existing instance
connection.Execute("insert [KeyLookup](Id, Name) values(@Id, @Name)", lookup);
Dapper usa el texto de comando para determinar qué miembros del objeto agregar: generalmente no agregará elementos innecesarios como Description
, IsActive
, CreationDate
porque el comando que hemos emitido claramente no los involucra, aunque existen casos en los que podría hacer eso, por ejemplo, si su comando contiene:
// TODO - removed for now; include the @Description in the insert
No intenta descubrir que lo anterior es solo un comentario.
Procedimientos almacenados
Los parámetros de los procedimientos almacenados funcionan exactamente igual, excepto que Dapper no puede tratar de determinar qué debe / no debe incluirse, todo lo disponible se trata como un parámetro. Por esa razón, usualmente se prefieren los tipos anónimos:
connection.Execute("KeyLookupInsert", new { id, name },
commandType: CommandType.StoredProcedure);
Valor en línea
A veces, la conveniencia de un parámetro (en términos de mantenimiento y expresividad) puede ser superada por su costo en el rendimiento para tratarlo como un parámetro. Por ejemplo, cuando el tamaño de la página está fijado por una configuración. O un valor de estado coincide con un valor enum
. Considerar:
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 });
El único parámetro real aquí es customerId
, los otros dos son pseudo parámetros que no cambiarán realmente. A menudo, el RDBMS puede hacer un mejor trabajo si detecta estas constantes. Dapper tiene una sintaxis especial para esto - {=name}
lugar de @name
- que solo se aplica a los tipos numéricos. (Esto minimiza cualquier superficie de ataque de la inyección de SQL). Un ejemplo es el siguiente:
var orders = connection.Query<Order>(@"
select top {=count} *
from Orders
where CustomerId = @customerId
and Status = {=open}", new { customerId, count = PageSize, open = OrderStatus.Open });
Dapper reemplaza los valores con literales antes de emitir el SQL, por lo que RDBMS realmente ve algo como:
select top 10 *
from Orders
where CustomerId = @customerId
and Status = 3
Esto es particularmente útil cuando se permite que los sistemas RDBMS no solo tomen mejores decisiones, sino que también abran planes de consulta que impiden los parámetros reales. Por ejemplo, si un predicado de columna es contra un parámetro, entonces no se puede usar un índice filtrado con valores específicos en esas columnas. Esto se debe a que la siguiente consulta puede tener un parámetro aparte de uno de esos valores especificados.
Con valores literales, el optimizador de consultas puede hacer uso de los índices filtrados, ya que sabe que el valor no puede cambiar en futuras consultas.
Lista de expansiones
Un escenario común en las consultas de base de datos es IN (...)
donde la lista aquí se genera en tiempo de ejecución. La mayoría de los RDBMS carecen de una buena metáfora para esto, y no existe una solución universal de RDBMS cruzados para esto. En su lugar, Dapper proporciona una suave expansión automática de comandos Todo lo que se requiere es un valor de parámetro suministrado que sea IEnumerable
. Un comando relacionado con @foo
se expande a (@foo0,@foo1,@foo2,@foo3)
(para una secuencia de 4 elementos). El uso más común de esto sería IN
:
int[] orderIds = ...
var orders = connection.Query<Order>(@"
select *
from Orders
where Id in @orderIds", new { orderIds });
Esto luego se expande automáticamente para emitir el SQL apropiado para la recuperación de varias filas:
select *
from Orders
where Id in (@orderIds0, @orderIds1, @orderIds2, @orderIds3)
con los parámetros @orderIds0
etc. que se agregan como valores tomados del arrray. Tenga en cuenta que el hecho de que originalmente no sea un SQL válido es intencional, para garantizar que esta característica no se use por error. Esta característica también funciona correctamente con la sugerencia de consulta OPTIMIZE FOR
/ UNKNOWN
en SQL Server; si utiliza:
option (optimize for
(@orderIds unknown))
expandirá esto correctamente a:
option (optimize for
(@orderIds0 unknown, @orderIds1 unknown, @orderIds2 unknown, @orderIds3 unknown))
Realizar operaciones contra múltiples conjuntos de entrada
A veces, quieres hacer lo mismo varias veces. Dapper admite esto en el método de Execute
si el parámetro más externo (que generalmente es un solo tipo anónimo o una instancia de modelo de dominio) en realidad se proporciona como una secuencia IEnumerable
. Por ejemplo:
Order[] orders = ...
// update the totals
connection.Execute("update Orders set Total=@Total where Id=@Id", orders);
Aquí, Dapper solo está haciendo un simple bucle en nuestros datos, esencialmente como si hubiéramos hecho:
Order[] orders = ...
// update the totals
foreach(Order order in orders) {
connection.Execute("update Orders set Total=@Total where Id=@Id", order);
}
Este uso se vuelve particularmente interesante cuando se combina con la API async
en una conexión que está configurada explícitamente para todos los "Conjuntos de resultados activos múltiples". En este uso, Dapper canalizará las operaciones automáticamente, por lo que no está pagando el costo de latencia por fila. Esto requiere un uso un poco más complicado,
await connection.ExecuteAsync(
new CommandDefinition(
"update Orders set Total=@Total where Id=@Id",
orders, flags: CommandFlags.Pipelined))
Sin embargo, tenga en cuenta que es posible que también desee investigar los parámetros con valores de tabla.
Parámetros pseudo-posicionales (para proveedores que no admiten parámetros nombrados)
Algunos proveedores de ADO.NET (en particular: OleDB) no admiten parámetros con nombre ; los parámetros se especifican en su lugar solo por posición , con el ?
titular de lugar. Dapper no sabría qué miembro usar para estos, por lo que Dapper permite una sintaxis alternativa, ?foo?
; esto sería lo mismo que @foo
o :foo
en otras variantes de SQL, excepto que Dapper reemplazará el token de parámetro completamente con ?
Antes de ejecutar la consulta.
Esto funciona en combinación con otras características como la expansión de lista, por lo que lo siguiente es válido:
string region = "North";
int[] users = ...
var docs = conn.Query<Document>(@"
select * from Documents
where Region = ?region?
and OwnerId in ?users?", new { region, users }).AsList();
Los miembros .region
y .users
se utilizan en consecuencia, y el SQL emitido es (por ejemplo, con 3 usuarios):
select * from Documents
where Region = ?
and OwnerId in (?,?,?)
Sin embargo, tenga en cuenta que Dapper no permite que se use el mismo parámetro varias veces al usar esta función; esto es para evitar tener que agregar el mismo valor de parámetro (que podría ser grande) varias veces. Si necesita referirse al mismo valor varias veces, considere declarar una variable, por ejemplo:
declare @id int = ?id?; // now we can use @id multiple times in the SQL
Si las variables no están disponibles, puede usar nombres de miembros duplicados en los parámetros. Esto también hará que sea obvio que el valor se envía varias veces:
int id = 42;
connection.Execute("... where ParentId = $id0$ ... SomethingElse = $id1$ ...",
new { id0 = id, id1 = id });