You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is way too complicated - and you need to iterate. Should be a one-liner.
using(IDataReaderreader=Database.ExecuteQuery(cmd,string.Format("SELECT {0}, {1}, {2} FROM {3} WHERE {1} = 0",Database.QuoteColumnNameIfRequired("Id"),Database.QuoteColumnNameIfRequired("Archive"),Database.QuoteColumnNameIfRequired("GenericDefinition"),Database.QuoteTableNameIfRequired("Common_Screens"))))
Protection against SQL injection for interpolated values.
Strongly typed results through generics or tuples.
No manual IDataReader iteration or disposal required.
FormattableString vs. custom interpolated string handler
FormattableString would probably be sufficient for the initial implementation. It already provides access to the format string and its arguments without losing their types.
A custom interpolated string handler could be introduced later for better performance, fewer allocations, or more specialized compile-time handling.
Recommendation: Start with FormattableString and a generic Query<T> method. This should solve both issues: cumbersome SQL construction and manual result mapping, without introducing unnecessary complexity.
Instead of using FormattableString, we could implement a custom interpolated string handler for SQL queries.
This provides additional flexibility, better performance, and more control over how interpolated values are processed.
1. Type-specific handling
A custom handler allows specialized AppendFormatted overloads for SQL identifiers and regular values.
[InterpolatedStringHandler]
public ref struct SqlQueryHandler
{
public void AppendLiteral(string text)
{
// Append raw SQL
}
public void AppendFormatted(SqlColumn column)
{
// Quote column name
}
public void AppendFormatted(SqlTable table)
{
// Quote table name
}
public void AppendFormatted<T>(T value)
{
// Add SQL parameter
}
}
The compiler automatically selects the appropriate overload without requiring runtime inspection of an object[].
2. Direct access to the database dialect
The handler can receive the database instance through its constructor:
public SqlQueryHandler(
int literalLength,
int formattedCount,
Database database)
{
// Use database dialect for quoting
}
The query method could use:
public IEnumerable<T> Query<T>(
[InterpolatedStringHandlerArgument("")]
SqlQueryHandler sql,
Func<IDataReader, T> mapper)
This allows database-specific quoting and parameter generation directly during interpolation.
3. Custom formatting syntax
A particularly interesting option would be to use format specifiers instead of explicit wrappers:
var users = Database.Query(
$"""
SELECT {"Id":column}, {"Name":column}
FROM {"Users":table}
WHERE {"Active":column} = {true}
""",
r => new User(r.GetInt32(0), r.GetString(1)));
The handler could interpret these format specifiers:
public void AppendFormatted(
string value,
string? format = null)
{
switch (format)
{
case "column":
AppendColumn(value);
break;
case "table":
AppendTable(value);
break;
default:
AppendParameter(value);
break;
}
}
This avoids calls such as Sql.Col("Id") or Sql.Table("Users") and makes the SQL much more readable.
Note that FormattableString also preserves format specifiers, so this syntax alone does not require a custom handler.
4. Performance advantages
Feature
FormattableString
Custom handler
Intermediate object[]
Typically yes
No
Boxing of value types
Typically yes
Can avoid
Specialized overloads
No
Yes
Database dialect access
During processing
During interpolation
Automatic parameterization
Yes
Yes
Custom format specifiers
Yes
Yes
Compile-time type restrictions
Limited
More control
Implementation complexity
Low
Higher
For migration operations, database execution time will usually dominate, so performance improvements may not be significant.
5. Compile-time type checking
A custom handler can provide overloads only for supported SQL argument types.
This allows unsupported interpolation types to produce compilation errors instead of runtime errors.
However, arbitrary SQL literals would still be possible, and custom format specifiers such as :column would not automatically be compile-time validated.
Recommendation
I would consider a custom interpolated string handler for Migrator.NET because it offers a clean API with database-specific identifier quoting and automatic parameterization.
The proposed syntax would be:
var screens = Database.Query(
$"""
SELECT {"Id":column}, {"GenericDefinition":column}
FROM {"Common_Screens":table}
WHERE {"Archive":column} = {false}
""",
r => (
Id: r.GetInt32(0),
Definition: r.GetString(1)
));
Main advantages:
No manual quoting of table or column names.
Automatic SQL parameter generation.
Database-specific SQL handling.
Reduced allocations and potentially less boxing.
Type-specific interpolation handling.
Cleaner and more readable query code.
The main drawback is increased implementation complexity. Since handlers are ref struct types, asynchronous APIs also require some care around handler lifetimes.
Both approaches are viable, but a custom handler offers more flexibility for future extensions.
Add interpolated SQL queries with DTO and tuple results #208: the SQL interpolation proposal using FormattableString, with both Sql.Col / Sql.Table wrappers and :column / :table formats, automatic parameters, and DTO or tuple/custom-mapper results.
Typed API examples:
varscreens=provider.Select<ScreenDto>("Common_Screens",
x =>(!x.Archive&&x.Id>4)||x.Definition.Contains("aa"));intupdated=provider.Update<ScreenDto>("Common_Screens",
x =>newScreenDto{Archive=true},
x =>x.Definition.StartsWith("old"));intdeleted=provider.Delete<ScreenDto>("Common_Screens",
x =>x.Archive&&x.Definition.EndsWith(".obsolete"));
Interpolation example:
varscreens=provider.Query<ScreenDto>($""" SELECT {"Id":column}, {"Archive":column}, {"GenericDefinition":column} FROM {"Common_Screens":table} WHERE {"Archive":column} = {false}""");
Both PR descriptions contain API signatures, DTO definitions, usage examples, validation and supported limits. #208 builds on #206's shared mapping/parameter infrastructure: review/merge #206 first, then retarget #208 to master.
Activity
jogibear9988 commentedon Oct 7, 2026
there exists a select api
jogibear9988 commentedon Oct 7, 2026
Suggestion: Simplify SQL queries using interpolated strings
We could introduce a new query API based on C# interpolated strings (
FormattableStringor a custom interpolated string handler).This would make SQL queries much easier to write while automatically handling database-specific identifier quoting and SQL parameters.
Example
Instead of manually quoting identifiers and using
string.Format:We could write:
Proposed implementation
Use
FormattableStringinitially:Introduce lightweight wrappers for SQL identifiers:
The query implementation would handle interpolated arguments differently depending on their type:
Benefits
Much cleaner and more readable SQL queries.
Automatic database-specific quoting (SQL Server, PostgreSQL, Oracle, SQLite, etc.).
Automatic parameterization of values.
Protection against SQL injection for interpolated values.
Strongly typed results through generics or tuples.
No manual
IDataReaderiteration or disposal required.FormattableString vs. custom interpolated string handler
FormattableStringwould probably be sufficient for the initial implementation. It already provides access to the format string and its arguments without losing their types.A custom interpolated string handler could be introduced later for better performance, fewer allocations, or more specialized compile-time handling.
Recommendation: Start with
FormattableStringand a genericQuery<T>method. This should solve both issues: cumbersome SQL construction and manual result mapping, without introducing unnecessary complexity.jogibear9988 commentedon Oct 7, 2026
Alternative: Custom Interpolated String Handler
Instead of using
FormattableString, we could implement a custom interpolated string handler for SQL queries.This provides additional flexibility, better performance, and more control over how interpolated values are processed.
1. Type-specific handling
A custom handler allows specialized
AppendFormattedoverloads for SQL identifiers and regular values.The compiler automatically selects the appropriate overload without requiring runtime inspection of an
object[].2. Direct access to the database dialect
The handler can receive the database instance through its constructor:
The query method could use:
This allows database-specific quoting and parameter generation directly during interpolation.
3. Custom formatting syntax
A particularly interesting option would be to use format specifiers instead of explicit wrappers:
The handler could interpret these format specifiers:
This avoids calls such as
Sql.Col("Id")orSql.Table("Users")and makes the SQL much more readable.Note that
FormattableStringalso preserves format specifiers, so this syntax alone does not require a custom handler.4. Performance advantages
For migration operations, database execution time will usually dominate, so performance improvements may not be significant.
5. Compile-time type checking
A custom handler can provide overloads only for supported SQL argument types.
This allows unsupported interpolation types to produce compilation errors instead of runtime errors.
However, arbitrary SQL literals would still be possible, and custom format specifiers such as
:columnwould not automatically be compile-time validated.Recommendation
I would consider a custom interpolated string handler for Migrator.NET because it offers a clean API with database-specific identifier quoting and automatic parameterization.
The proposed syntax would be:
Main advantages:
No manual quoting of table or column names.
Automatic SQL parameter generation.
Database-specific SQL handling.
Reduced allocations and potentially less boxing.
Type-specific interpolation handling.
Cleaner and more readable query code.
The main drawback is increased implementation complexity. Since handlers are
ref structtypes, asynchronous APIs also require some care around handler lifetimes.Both approaches are viable, but a custom handler offers more flexibility for future extensions.
JaBistDuNarrisch commentedon Oct 8, 2026
What I mean is sth. like
jogibear9988 commentedon Oct 14, 2026
The implementation is split into these two pull requests:
Select<T>,Update<T>andDelete<T>, including stringContains,StartsWithandEndsWith.FormattableString, with bothSql.Col/Sql.Tablewrappers and:column/:tableformats, automatic parameters, and DTO or tuple/custom-mapper results.Typed API examples:
Interpolation example:
Both PR descriptions contain API signatures, DTO definitions, usage examples, validation and supported limits. #208 builds on #206's shared mapping/parameter infrastructure: review/merge #206 first, then retarget #208 to master.