Table of Contents

Builder

The Builder is a versatile tool for constructing static, dynamic, complex SQL queries, and stored procedures.

The Create method on the SimpleSqlBuilder or ISimpleBuilder (when using dependency injection) creates a new Builder instance. It accepts a SQL query as one of its parameters and returns a new Builder instance.

The SQL query can be a static string or an interpolated string. The Builder parses the SQL query and extracts the parameters from it. The parameters can be accessed via the Parameters property, and the generated SQL query can be accessed via the Sql property.

Static SQL

using Dapper.SimpleSqlBuilder;

int userTypeId = 10;
int age = 25;

var builder = SimpleBuilder.Create($@"
SELECT * FROM User
WHERE UserTypeId = {userTypeId} AND Age >= {age}");

The generated SQL will be:

SELECT * FROM User
WHERE UserTypeId = @p0 AND Age >= @p1

For newer versions of C# (11 and later), you can also use raw string literals with string interpolation to build your SQL queries, instead of verbatim string literals. See the example below.

var builder = SimpleBuilder.Create($"""
SELECT * FROM User
WHERE UserTypeId = {userTypeId} AND Age >= {age}
""");

Dynamic SQL

Interpolated String Concatenation

You can concatenate multiple interpolated strings to build your dynamic SQL.

var users = GetUsers(4, null, 25);

IEnumerable<User> GetUsers(int userTypeId, string role, int? minAge)
{
    var builder = SimpleBuilder.Create($"SELECT * FROM User");
    builder += $" WHERE UserTypeId = {userTypeId}";

    if (role is not null)
    {
        builder += $" AND Role = {role}";
    }

    if (minAge.HasValue)
    {
        builder += $" AND Age >= {minAge}";
    }

    return dbConnection.Query<User>(builder.Sql, builder.Parameters);
}

The generated SQL will be:

SELECT * FROM User WHERE UserTypeId = @p0 AND Age >= @p1

Builder Chaining

If you prefer an alternative to interpolated string concatenation, you can use the Append, AppendIntact, and AppendNewLine methods, which can be chained.

int userTypeId = 4;
string role = "User";

var builder = SimpleBuilder.Create($"SELECT * FROM User")
    .AppendNewLine($"WHERE UserTypeId = {userTypeId}")
    .Append($"OR Role = {role}")
    .AppendNewLine($"ORDER BY FirstName ASC");

The generated SQL will be:

SELECT * FROM User
WHERE UserTypeId = @p0 OR Role = @p1
ORDER BY FirstName ASC

You can also use it with conditional statements. The Append, AppendIntact, and AppendNewLine methods all have conditional overloads. This is useful when you want to append a statement only if a condition is met.

var users = GetUsers(4, "Admin", null, true);

IEnumerable<User> GetUsers(int userTypeId, string role, int? minAge, bool orderByName)
{
    var builder = SimpleBuilder.Create()
        .AppendIntact($"SELECT * FROM User")
        .AppendNewLine($"WHERE UserTypeId = {userTypeId}")
        .Append(role is not null, $"AND Role = {role}")
        .Append(minAge.HasValue, $"AND Age >= {minAge}")
        .AppendNewLine(orderByName, $"ORDER BY FirstName, LastName");

    return dbConnection.Query<User>(builder.Sql, builder.Parameters);
}

The generated SQL will be:

SELECT * FROM User
WHERE UserTypeId = @p0 AND Role = @p1
ORDER BY FirstName, LastName
Note

The Append method adds a space before the SQL text by default. You can use the AppendIntact method if you do not want this behaviour.

INSERT, UPDATE, and DELETE Statements

Insert

You can perform INSERT operations with the Builder as seen in the example below.

var user = new User { FirstName = "John", LastName = "Doe", UserTypeId = 4 };

var builder = SimpleBuilder.Create($@"
INSERT INTO User (FirstName, LastName, UserTypeId)
VALUES ({user.FirstName}, {user.LastName}, {user.UserTypeId})");

// Execute the query with Dapper
dbConnection.Execute(builder.Sql, builder.Parameters);

The generated SQL will be:

INSERT INTO User (FirstName, LastName, UserTypeId)
VALUES (@p0, @p1, @p2)

Update

You can perform UPDATE operations with the Builder as seen in the example below.

int id = 1;
string role = "Admin";

var builder = SimpleBuilder.Create($@"
UPDATE User 
SET Role = {role}
WHERE Id = {id}");

The generated SQL will be:

UPDATE User
SET Role = @p0
WHERE Id = @p1

Delete

You can perform DELETE operations with the builder as seen in the example below.

int id = 1;
var builder = SimpleBuilder.Create($"DELETE FROM User WHERE Id = {id}");

The generated SQL will be:

DELETE FROM User WHERE Id = @p0

Stored Procedures

You can execute stored procedures with the Builder as seen in the example below.

var user = new User { FirstName = "John", LastName = "Doe", UserTypeId = 4 };

var builder = SimpleBuilder.Create($"CreateUserProc")
    .AddParameter("FirstName", user.FirstName)
    .AddParameter("LastName", user.LastName)
    .AddParameter("UserTypeId", user.UserTypeId)
    .AddParameter("Id", dbType: DbType.Int32, direction: ParameterDirection.Output)
    .AddParameter("Result", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

// Execute the stored procedure with Dapper
dbConnection.Execute(builder.Sql, builder.Parameters, commandType: CommandType.StoredProcedure);

// Get the output and return values
int id = builder.GetValue<int>("Id");
int result = builder.GetValue<int>("Result");

Builder Reset

There are scenarios where you may want to reuse the Builder without creating a new instance. This can be achieved by calling the Reset method on the Builder instance as seen in the example below.

int id = 1;
var builder = SimpleBuilder.Create($"SELECT * FROM User WHERE Id = {id}");

// Execute the query with Dapper
var user = dbConnection.QuerySingle<User>(builder.Sql, builder.Parameters);

// Reset the builder
builder.Reset();

// Reuse the builder
builder.AppendIntact($"DELETE FROM User WHERE Id = {id}");

// Execute the query with Dapper
dbConnection.Execute(builder.Sql, builder.Parameters);