using Microsoft.Data.SqlClient;
using System.Data.Common;
namespace SqlInterpolation;
static void Main(string[] args)
var procedureName = "procedure1";
var v1 = new SqlParameter("@v1", 1);
var v2 = new SqlParameter("@v2", 2);
var s = new SqlFormattableString($"EXEC {SqlLiteral.From(procedureName)} {v1} {v2}");
Console.WriteLine(s.Format);
public static SqlLiteral From(string v)
return new SqlLiteral(v);
public readonly string Value;
public SqlLiteral(string v)
public override string ToString()
class SqlFormattableString : FormattableString
private object[] Arguments;
public SqlFormattableString(FormattableString input)
var paramNames = new string[input.ArgumentCount];
var argList = new List<object>();
foreach (var item in input.GetArguments())
if (item is DbParameter p)
paramNames[i] = (p.ParameterName.StartsWith("@") ? p.ParameterName : "@" + p.ParameterName) + "={" + argList.Count + "}";
} else if (item is SqlLiteral l) {
paramNames[i] = "{" + argList.Count + "}";
this.Arguments = argList.ToArray();
this.format = string.Format(input.Format, paramNames);
public override int ArgumentCount => this.Arguments.Length;
public override string Format => this.format;
public override object? GetArgument(int index)
return this.Arguments[index];
public override object?[] GetArguments()
public override string ToString(IFormatProvider? formatProvider)
return string.Format(this.format, this.Arguments);