Parameters and temporary tables
Two helpers turn an ordinary interpolated string into a parameterized statement: Parameter(value) binds a value, and TemporaryTable(values) puts a whole collection into the database for the statement to join against. Both are usable with any method that accepts an InterpolatedSqlStatement.
Parameters via interpolated strings
All extension methods accept interpolated strings where parameter values are captured via Parameter(value):
var lowStockThreshold = configuration.Thresholds.LowStock;
var lowStockProductInfos = connection.Query<(long ProductId, int UnitsInStock)>(
$"""
SELECT Id, UnitsInStock
FROM Product
WHERE UnitsInStock < {Parameter(lowStockThreshold)}
"""
);
This prevents SQL injection and keeps the SQL readable.
On-the-fly temporary tables via interpolated strings
Caution
Warning for Oracle users
This feature creates private temporary tables and drops them after use. In Oracle, DDL statements cause an
implicit commit of the current transaction — so inside an explicit transaction it is committed twice:
once when the temporary table is created and once when it is dropped.
For that reason the feature is disabled by default for Oracle and using it throws. To enable it anyway,
set RentADeveloper.DbConnectionPlus.DatabaseAdapters.Oracle.OracleDatabaseAdapter.AllowTemporaryTables to
true — and avoid the feature inside explicit transactions.
Note
Note for MySQL users
Temporary tables are populated with MySqlBulkCopy, so the connection string needs
AllowLoadLocalInfile=true and the server needs local_infile enabled (e.g. SET GLOBAL local_infile=1).
Create a temporary table on the fly from an IEnumerable<T> and use it in statements via
TemporaryTable(values):
var retiredSupplierIds = suppliers.Where(a => a.IsRetired).Select(a => a.Id);
var retiredSupplierProducts = connection.Query<Product>(
$"""
SELECT *
FROM Product
WHERE SupplierId IN (
SELECT Value
FROM {TemporaryTable(retiredSupplierIds)}
)
"""
);
Complex objects are also supported - the library creates a temporary table with appropriate columns and types:
class OrderItem
{
public long ProductId { get; set; }
public DateTime OrderDate { get; set; }
}
var orderItems = GetOrderItems();
var sixMonthsAgo = DateTime.UtcNow.AddMonths(-6);
var productsOrderedInPastSixMonths = connection.Query<Product>(
$"""
SELECT *
FROM Product
WHERE EXISTS (
SELECT 1
FROM {TemporaryTable(orderItems)} TOrderItem
WHERE TOrderItem.ProductId = Product.Id AND
TOrderItem.OrderDate >= {Parameter(sixMonthsAgo)}
)
"""
);
Special helpers
The following special helpers can be used with any DbConnectionPlus extension method that accepts an instance of
InterpolatedSqlStatement.
Parameter(value)
Use Parameter(value) to pass a value in an interpolated string as a parameter to an SQL statement.
var lowStockThreshold = configuration.Thresholds.LowStock;
using var lowStockProductsReader = connection.ExecuteReader(
$"""
SELECT *
FROM Product
WHERE UnitsInStock < {Parameter(lowStockThreshold)}
"""
);
This adds a parameter holding the value of lowStockThreshold to the SQL statement, and replaces the
{Parameter(value)} expression with the parameter's name.
The parameter name is inferred from the expression passed to Parameter(value) - here LowStockThreshold. If
no name can be inferred (e.g. Parameter(42)), a generic name like Parameter_1, Parameter_2 and so on is
used.
Enum values are serialized as strings or as integers according to EnumSerializationMode.
TemporaryTable(values)
Use TemporaryTable(values) to pass a sequence of scalar values or complex objects in an interpolated string as a
temporary table to an SQL statement.
A sequence of scalar values (e.g. string, int, DateTime, enums and so on) produces a temporary table
with a single column named Value, typed to match the passed values:
var retiredSupplierIds = suppliers.Where(a => a.IsRetired).Select(a => a.Id);
using var retiredSupplierProductsReader = connection.ExecuteReader(
$"""
SELECT *
FROM Product
WHERE SupplierId IN (
SELECT Value
FROM {TemporaryTable(retiredSupplierIds)}
)
"""
);
CREATE TABLE #RetiredSupplierIds_48d42afd5d824a27bd9352676ab6c198
(
Value BIGINT
)
A sequence of complex objects produces one column per public property, named and typed after that property:
class OrderItem
{
public long ProductId { get; set; }
public DateTime OrderDate { get; set; }
}
var orderItems = GetOrderItems();
var sixMonthsAgo = DateTime.UtcNow.AddMonths(-6);
using var productsOrderedInPastSixMonthsReader = connection.ExecuteReader(
$"""
SELECT *
FROM Product
WHERE EXISTS (
SELECT 1
FROM {TemporaryTable(orderItems)} TOrderItem
WHERE TOrderItem.ProductId = Product.Id AND
TOrderItem.OrderDate >= {Parameter(sixMonthsAgo)}
)
"""
);
CREATE TABLE #OrderItems_d6545835d97148ab93709efe9ba1f110
(
ProductId BIGINT,
OrderDate DATETIME2
)
The table name is inferred from the expression passed to TemporaryTable(values) and suffixed with a new Guid
to avoid naming conflicts (e.g. OrderItems_395c98f203514e81aa0098ec7f13e8a2); if no name can be inferred,
Values is used instead. The {TemporaryTable(values)} expression is replaced with that name in the SQL
statement.
Enum values - passed directly or as properties of complex objects - are serialized according to
EnumSerializationMode, and the column is typed NVARCHAR(200) for Strings and
INT for Integers.