Skip to content

Commit fcfee2f

Browse files
committed
Code optimization
1 parent f20f356 commit fcfee2f

7 files changed

Lines changed: 492 additions & 462 deletions

src/Sql/CommandBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ SELECT 1
141141

142142
var parameter = new Parameter(UsePositionalParameters ? "?1" : GetParameterName(identityColumn.Name), id);
143143
var text = $"""
144-
SELECT {(columns is null || columns.Length == 0 ? "*" : string.Join(", ", columns.Select(QuoteIdentifier)))}
144+
SELECT {(columns.Length == 0 ? "*" : string.Join(", ", columns.Select(QuoteIdentifier)))}
145145
FROM {GetTableName(table)}
146146
WHERE {QuoteIdentifier(identityColumn.Name)} = {(UsePositionalParameters ? "?" : parameter.Name)}
147147
""";
@@ -184,7 +184,7 @@ .. fields.Select((field, index) => (UsePositionalParameters ? $"?{index}" : GetP
184184
var table = Mapper.Instance.GetTable<T>();
185185
var identityColumn = table.IdentityColumn ?? throw new InvalidOperationException("The identity column could not be found.");
186186

187-
var fields = (columns is null || columns.Length == 0 ? table.Columns.Values : table.Columns.Values.Where(column => columns.Contains(column.Name)))
187+
var fields = (columns.Length == 0 ? table.Columns.Values : table.Columns.Values.Where(column => columns.Contains(column.Name)))
188188
.Where(column => column.CanRead && !column.IsComputed)
189189
.ToArray();
190190

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
namespace Belin.Sql;
2+
3+
using Belin.Sql.Reflection;
4+
using System.Data;
5+
6+
/// <summary>
7+
/// Provides extension members for database connections.
8+
/// </summary>
9+
public static partial class ConnectionExtensions {
10+
11+
/// <summary>
12+
/// Deletes the specified entity.
13+
/// </summary>
14+
/// <typeparam name="T">The entity type.</typeparam>
15+
/// <param name="connection">The connection to the data source.</param>
16+
/// <param name="instance">The entity to delete.</param>
17+
/// <param name="options">The command options.</param>
18+
/// <returns><see langword="true"/> if the specified entity has been deleted, otherwise <see langword="false"/>.</returns>
19+
public static bool Delete<T>(this IDbConnection connection, T instance, CommandOptions? options = null) where T: new() {
20+
var (text, parameters) = new CommandBuilder(connection).GetDeleteCommand(instance);
21+
return Execute(connection, text, parameters, options) > 0;
22+
}
23+
24+
/// <summary>
25+
/// Deletes the specified entity.
26+
/// </summary>
27+
/// <typeparam name="T">The entity type.</typeparam>
28+
/// <param name="connection">The connection to the data source.</param>
29+
/// <param name="instance">The entity to delete.</param>
30+
/// <param name="options">The command options.</param>
31+
/// <returns><see langword="true"/> if the specified entity has been deleted, otherwise <see langword="false"/>.</returns>
32+
public static async Task<bool> DeleteAsync<T>(this IDbConnection connection, T instance, CommandOptions? options = null) where T: new() {
33+
var (text, parameters) = new CommandBuilder(connection).GetDeleteCommand(instance);
34+
return await ExecuteAsync(connection, text, parameters, options) > 0;
35+
}
36+
37+
/// <summary>
38+
/// Checks whether an entity with the specified primary key exists.
39+
/// </summary>
40+
/// <typeparam name="T">The entity type.</typeparam>
41+
/// <param name="connection">The connection to the data source.</param>
42+
/// <param name="id">The primary key value.</param>
43+
/// <param name="options">The command options.</param>
44+
/// <returns><see langword="true"/> if an entity with the specified primary key exists, otherwise <see langword="false"/>.</returns>
45+
public static bool Exists<T>(this IDbConnection connection, object id, CommandOptions? options = null) where T: new() {
46+
var (text, parameters) = new CommandBuilder(connection).GetExistsCommand<T>(id);
47+
return ExecuteScalar<bool>(connection, text, parameters, options);
48+
}
49+
50+
/// <summary>
51+
/// Checks whether an entity with the specified primary key exists.
52+
/// </summary>
53+
/// <typeparam name="T">The entity type.</typeparam>
54+
/// <param name="connection">The connection to the data source.</param>
55+
/// <param name="id">The primary key value.</param>
56+
/// <param name="options">The command options.</param>
57+
/// <returns><see langword="true"/> if an entity with the specified primary key exists, otherwise <see langword="false"/>.</returns>
58+
public static async Task<bool> ExistsAsync<T>(this IDbConnection connection, object id, CommandOptions? options = null) where T: new() {
59+
var (text, parameters) = new CommandBuilder(connection).GetExistsCommand<T>(id);
60+
return await ExecuteScalarAsync<bool>(connection, text, parameters, options);
61+
}
62+
63+
/// <summary>
64+
/// Finds an entity with the specified primary key.
65+
/// </summary>
66+
/// <typeparam name="T">The entity type.</typeparam>
67+
/// <param name="connection">The connection to the data source.</param>
68+
/// <param name="id">The primary key value.</param>
69+
/// <param name="columns">The list of columns to select. By default, all columns.</param>
70+
/// <param name="options">The command options.</param>
71+
/// <returns>The entity with the specified primary key, or <see langword="null"/> if not found.</returns>
72+
public static T? Find<T>(this IDbConnection connection, object id, string[]? columns = null, CommandOptions? options = null) where T: new() {
73+
var (text, parameters) = new CommandBuilder(connection).GetFindCommand<T>(id, columns ?? []);
74+
return QuerySingleOrDefault<T>(connection, text, parameters, options);
75+
}
76+
77+
/// <summary>
78+
/// Finds an entity with the specified primary key.
79+
/// </summary>
80+
/// <typeparam name="T">The entity type.</typeparam>
81+
/// <param name="connection">The connection to the data source.</param>
82+
/// <param name="id">The primary key value.</param>
83+
/// <param name="columns">The list of columns to select. By default, all columns.</param>
84+
/// <param name="options">The command options.</param>
85+
/// <returns>The entity with the specified primary key, or <see langword="null"/> if not found.</returns>
86+
public static async Task<T?> FindAsync<T>(this IDbConnection connection, object id, string[]? columns = null, CommandOptions? options = null) where T: new() {
87+
var (text, parameters) = new CommandBuilder(connection).GetFindCommand<T>(id, columns ?? []);
88+
return await QuerySingleOrDefaultAsync<T>(connection, text, parameters, options);
89+
}
90+
91+
/// <summary>
92+
/// Inserts the specified entity.
93+
/// </summary>
94+
/// <typeparam name="T">The entity type.</typeparam>
95+
/// <param name="connection">The connection to the data source.</param>
96+
/// <param name="instance">The entity to insert.</param>
97+
/// <param name="options">The command options.</param>
98+
/// <returns>The generated primary key value.</returns>
99+
public static long Insert<T>(this IDbConnection connection, T instance, CommandOptions? options = null) where T: new() {
100+
var (text, parameters) = new CommandBuilder(connection).GetInsertCommand(instance);
101+
var id = ExecuteScalar<long>(connection, text, parameters, options);
102+
if (Mapper.Instance.GetTable<T>().IdentityColumn is ColumnInfo column) column.SetValue(instance, Mapper.ChangeType(id, column));
103+
return id;
104+
}
105+
106+
/// <summary>
107+
/// Inserts the specified entity.
108+
/// </summary>
109+
/// <typeparam name="T">The entity type.</typeparam>
110+
/// <param name="connection">The connection to the data source.</param>
111+
/// <param name="instance">The entity to insert.</param>
112+
/// <param name="options">The command options.</param>
113+
/// <returns>The generated primary key value.</returns>
114+
public static async Task<long> InsertAsync<T>(this IDbConnection connection, T instance, CommandOptions? options = null) where T: new() {
115+
var (text, parameters) = new CommandBuilder(connection).GetInsertCommand(instance);
116+
var id = await ExecuteScalarAsync<long>(connection, text, parameters, options);
117+
if (Mapper.Instance.GetTable<T>().IdentityColumn is ColumnInfo column) column.SetValue(instance, Mapper.ChangeType(id, column));
118+
return id;
119+
}
120+
121+
/// <summary>
122+
/// Updates the specified entity.
123+
/// </summary>
124+
/// <typeparam name="T">The entity type.</typeparam>
125+
/// <param name="connection">The connection to the data source.</param>
126+
/// <param name="instance">The entity to update.</param>
127+
/// <param name="columns">The list of columns to update. By default, all columns.</param>
128+
/// <param name="options">The command options.</param>
129+
/// <returns>The number of rows affected.</returns>
130+
public static int Update<T>(this IDbConnection connection, T instance, string[]? columns = null, CommandOptions? options = null) where T: new() {
131+
var (text, parameters) = new CommandBuilder(connection).GetUpdateCommand(instance, columns ?? []);
132+
return Execute(connection, text, parameters, options);
133+
}
134+
135+
/// <summary>
136+
/// Updates the specified entity.
137+
/// </summary>
138+
/// <typeparam name="T">The entity type.</typeparam>
139+
/// <param name="connection">The connection to the data source.</param>
140+
/// <param name="instance">The entity to update.</param>
141+
/// <param name="columns">The list of columns to update. By default, all columns.</param>
142+
/// <param name="options">The command options.</param>
143+
/// <returns>The number of rows affected.</returns>
144+
public static async Task<int> UpdateAsync<T>(this IDbConnection connection, T instance, string[]? columns = null, CommandOptions? options = null) where T: new() {
145+
var (text, parameters) = new CommandBuilder(connection).GetUpdateCommand(instance, columns ?? []);
146+
return await ExecuteAsync(connection, text, parameters, options);
147+
}
148+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
namespace Belin.Sql;
2+
3+
using System.Data;
4+
using System.Data.Common;
5+
6+
/// <summary>
7+
/// Provides extension members for database connections.
8+
/// </summary>
9+
public static partial class ConnectionExtensions {
10+
11+
/// <summary>
12+
/// Executes a parameterized SQL statement.
13+
/// </summary>
14+
/// <param name="connection">The connection to the data source.</param>
15+
/// <param name="text">The SQL query to be executed.</param>
16+
/// <param name="parameters">The parameters of the SQL query.</param>
17+
/// <param name="options">The command options.</param>
18+
/// <returns>The number of rows affected.</returns>
19+
public static int Execute(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null) {
20+
if (connection.State == ConnectionState.Closed) connection.Open();
21+
using var command = CreateCommand(connection, text, parameters, options);
22+
return command.ExecuteNonQuery();
23+
}
24+
25+
/// <summary>
26+
/// Executes a parameterized SQL statement.
27+
/// </summary>
28+
/// <param name="connection">The connection to the data source.</param>
29+
/// <param name="text">The SQL query to be executed.</param>
30+
/// <param name="parameters">The parameters of the SQL query.</param>
31+
/// <param name="options">The command options.</param>
32+
/// <param name="cancellationToken">The token to cancel the operation.</param>
33+
/// <returns>The number of rows affected.</returns>
34+
public static async Task<int> ExecuteAsync(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null, CancellationToken cancellationToken = default) {
35+
if (connection.State == ConnectionState.Closed) await ((DbConnection) connection).OpenAsync(cancellationToken);
36+
using var command = (DbCommand) CreateCommand(connection, text, parameters, options);
37+
return await command.ExecuteNonQueryAsync(cancellationToken);
38+
}
39+
40+
/// <summary>
41+
/// Executes a parameterized SQL query and returns a data reader.
42+
/// </summary>
43+
/// <param name="connection">The connection to the data source.</param>
44+
/// <param name="text">The SQL query to be executed.</param>
45+
/// <param name="parameters">The parameters of the SQL query.</param>
46+
/// <param name="options">The command options.</param>
47+
/// <returns>The data reader that can be used to access the results.</returns>
48+
public static IDataReader ExecuteReader(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null) {
49+
if (connection.State == ConnectionState.Closed) connection.Open();
50+
using var command = CreateCommand(connection, text, parameters, options);
51+
return command.ExecuteReader();
52+
}
53+
54+
/// <summary>
55+
/// Executes a parameterized SQL query and returns a data reader.
56+
/// </summary>
57+
/// <param name="connection">The connection to the data source.</param>
58+
/// <param name="text">The SQL query to be executed.</param>
59+
/// <param name="parameters">The parameters of the SQL query.</param>
60+
/// <param name="options">The command options.</param>
61+
/// <param name="cancellationToken">The token to cancel the operation.</param>
62+
/// <returns>The data reader that can be used to access the results.</returns>
63+
public static async Task<IDataReader> ExecuteReaderAsync(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null, CancellationToken cancellationToken = default) {
64+
if (connection.State == ConnectionState.Closed) await ((DbConnection) connection).OpenAsync(cancellationToken);
65+
using var command = (DbCommand) CreateCommand(connection, text, parameters, options);
66+
return await command.ExecuteReaderAsync(cancellationToken);
67+
}
68+
69+
/// <summary>
70+
/// Executes a parameterized SQL query that selects a single value.
71+
/// </summary>
72+
/// <param name="connection">The connection to the data source.</param>
73+
/// <param name="text">The SQL query to be executed.</param>
74+
/// <param name="parameters">The parameters of the SQL query.</param>
75+
/// <param name="options">The command options.</param>
76+
/// <returns>The first column of the first row.</returns>
77+
public static object? ExecuteScalar(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null) =>
78+
ExecuteScalar<object>(connection, text, parameters, options);
79+
80+
/// <summary>
81+
/// Executes a parameterized SQL query that selects a single value.
82+
/// </summary>
83+
/// <param name="connection">The connection to the data source.</param>
84+
/// <param name="text">The SQL query to be executed.</param>
85+
/// <param name="parameters">The parameters of the SQL query.</param>
86+
/// <param name="options">The command options.</param>
87+
/// <param name="cancellationToken">The token to cancel the operation.</param>
88+
/// <returns>The first column of the first row.</returns>
89+
public static async Task<object?> ExecuteScalarAsync(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null, CancellationToken cancellationToken = default) =>
90+
ExecuteScalarAsync<object>(connection, text, parameters, options, cancellationToken);
91+
92+
/// <summary>
93+
/// Executes a parameterized SQL query that selects a single value.
94+
/// </summary>
95+
/// <typeparam name="T">The type of object to return.</typeparam>
96+
/// <param name="connection">The connection to the data source.</param>
97+
/// <param name="text">The SQL query to be executed.</param>
98+
/// <param name="parameters">The parameters of the SQL query.</param>
99+
/// <param name="options">The command options.</param>
100+
/// <returns>The first column of the first row.</returns>
101+
public static T? ExecuteScalar<T>(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null) {
102+
if (connection.State == ConnectionState.Closed) connection.Open();
103+
using var command = CreateCommand(connection, text, parameters, options);
104+
var value = command.ExecuteScalar();
105+
return value is null || value is DBNull ? default : (T?) Mapper.ChangeType(value, typeof(T));
106+
}
107+
108+
/// <summary>
109+
/// Executes a parameterized SQL query that selects a single value.
110+
/// </summary>
111+
/// <typeparam name="T">The type of object to return.</typeparam>
112+
/// <param name="connection">The connection to the data source.</param>
113+
/// <param name="text">The SQL query to be executed.</param>
114+
/// <param name="parameters">The parameters of the SQL query.</param>
115+
/// <param name="options">The command options.</param>
116+
/// <param name="cancellationToken">The token to cancel the operation.</param>
117+
/// <returns>The first column of the first row.</returns>
118+
public static async Task<T?> ExecuteScalarAsync<T>(this IDbConnection connection, string text, ParameterCollection? parameters = null, CommandOptions? options = null, CancellationToken cancellationToken = default) {
119+
if (connection.State == ConnectionState.Closed) await ((DbConnection) connection).OpenAsync(cancellationToken);
120+
using var command = (DbCommand) CreateCommand(connection, text, parameters, options);
121+
var value = await command.ExecuteScalarAsync(cancellationToken);
122+
return value is null || value is DBNull ? default : (T?) Mapper.ChangeType(value, typeof(T));
123+
}
124+
}

0 commit comments

Comments
 (0)