RSCG – Breezy
RSCG – Breezy
name | Breezy |
nuget | https://www.nuget.org/packages/Breezy.SourceGenerator/ |
link | https://github.com/Ludovicdln/Breezy |
author | Ludovicdln |
ORM Mapper
This is how you can use Breezy .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath> </PropertyGroup> <ItemGroup> <PackageReference Include="Breezy.SourceGenerator" Version="1.0.1" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.1" /> </ItemGroup> </Project>
The code that you will use is
using var connection = new SqlConnection(); //in the order of the properties in Person.cs var persons = await connection.QueryAsync<Person>("SELECT Id,firstname, lastname FROM person");
namespace BreezyDemo; [Table("person")]//this is Breezy.Table public class Person { public int ID { get; set; } public string? FirstName { get; set; } public string? LastName { get; set; } }
global using Breezy; global using Microsoft.Data.SqlClient; global using BreezyDemo;
The code that is generated is
// <auto-generated /> using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Threading.Tasks; using System.Collections.Generic; namespace DbConnectionExtensions { public static class DbConnectionExtensions { /// <summary> /// Execute a command asynchronously using Task. /// </summary> /// <param name = "sql">The SQL to execute for the query.</param> /// <returns>The number of rows affected.</returns> public static async Task<int> ExecuteAsync(this DbConnection connection, string sql, CancellationToken cancellationToken = default) { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = sql; try { return await command.ExecuteNonQueryAsync(cancellationToken); } finally { if (wasClosed) connection.Close(); } } /// <summary> /// Execute a command asynchronously using Task. /// </summary> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "param">The parameters to pass, if any.</param> /// <returns>The number of rows affected.</returns> public static async Task<int> ExecuteAsync(this DbConnection connection, string sql, object param, CancellationToken cancellationToken = default) { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = sql; foreach (var property in param.GetType().GetProperties()) { var parameter = command.CreateParameter(); parameter.ParameterName = "@" + property.Name; parameter.Value = property.GetValue(param); command.Parameters.Add(parameter); } try { return await command.ExecuteNonQueryAsync(cancellationToken); } finally { if (wasClosed) connection.Close(); } } /// <summary> /// Execute a command asynchronously using Task. /// </summary> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "transaction">The transaction to use for this query.</param> /// <returns>The number of rows affected.</returns> public static async Task<int[]> ExecuteAsync(this DbConnection connection, string[] sql, DbTransaction transaction, CancellationToken cancellationToken = default) { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); var commands = new DbCommand[sql.Length]; for (var i = 0; i < sql.Length; i++) { await using var command = connection.CreateCommand(); command.CommandText = sql[i]; command.Transaction = transaction; commands[i] = command; } try { var results = new int[sql.Length]; for (var i = 0; i < commands.Length; i++) results[i] = await commands[i].ExecuteNonQueryAsync(cancellationToken); await transaction.CommitAsync(); return results; } catch (DbException e) { await transaction.RollbackAsync(); return Array.Empty<int>(); } finally { transaction.Dispose(); if (wasClosed) connection.Close(); } } /// <summary> /// Execute a command asynchronously using Task. /// </summary> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "param">The parameters to pass, if any.</param> /// <param name = "transaction">The transaction to use for this query.</param> /// <returns>The number of rows affected.</returns> public static async Task<int[]> ExecuteAsync(this DbConnection connection, string[] sql, object[] param, DbTransaction transaction, CancellationToken cancellationToken = default) { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); var commands = new DbCommand[sql.Length]; for (var i = 0; i < sql.Length; i++) { await using var command = connection.CreateCommand(); command.CommandText = sql[i]; command.Transaction = transaction; var paramt = param[i]; foreach (var property in paramt.GetType().GetProperties()) { var parameter = command.CreateParameter(); parameter.ParameterName = "@" + property.Name; parameter.Value = property.GetValue(paramt); command.Parameters.Add(parameter); } commands[i] = command; } try { var results = new int[sql.Length]; for (var i = 0; i < commands.Length; i++) results[i] = await commands[i].ExecuteNonQueryAsync(cancellationToken); await transaction.CommitAsync(); return results; } catch (DbException e) { await transaction.RollbackAsync(); return Array.Empty<int>(); } finally { transaction.Dispose(); if (wasClosed) connection.Close(); } } } }
// <auto-generated /> using System; namespace Breezy; public interface ICacheableQuery<T> where T : class { public Task<IEnumerable<T>> GetCacheableResultsAsync(IdentityQuery identityQuery); public Task SetCacheableResultsAsync(IdentityQuery identityQuery, IEnumerable<T> results); }
// <auto-generated /> using System; namespace Breezy; public class IdentityQuery : IEquatable<IdentityQuery> { private readonly int _hashCodeSql; private readonly int? _hashCodeParam; public IdentityQuery(string sql, object? param = null) => (_hashCodeSql, _hashCodeParam) = (sql.GetHashCode(), param?.GetHashCode()); public bool Equals(IdentityQuery? other) { if (ReferenceEquals(other, this)) return true; return this.GetHashCode() == other?.GetHashCode(); } public override string ToString() => $"{_hashCodeSql.ToString()}-{_hashCodeParam?.ToString()}"; public override bool Equals(object? obj) => Equals(obj as IdentityQuery); public override int GetHashCode() => HashCode.Combine(_hashCodeSql, _hashCodeParam); }
// <auto-generated /> using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Threading.Tasks; using System.Collections.Generic; namespace BreezyDemo { public static class PersonExtensions { /// <summary> /// Execute a query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of results to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<IEnumerable<Person>> QueryAsync<T>(this DbConnection connection, string sql, CancellationToken cancellationToken = default) where T : Person { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = sql; await using var reader = await command.ExecuteReaderAsync(cancellationToken: cancellationToken); var persons = new Dictionary<int, Person>(); try { while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { Person person = null; var person1Id = reader.IsDBNull(0) ? default : reader.GetInt32(0); if (!persons.TryGetValue(person1Id, out person)) { person = new Person() { ID = person1Id, FirstName = reader.IsDBNull(1) ? default : reader.GetString(1), LastName = reader.IsDBNull(2) ? default : reader.GetString(2), }; persons.Add(person1Id, person); } } return persons.Values; } finally { reader.Close(); if (wasClosed) connection.Close(); } } /// <summary> /// Execute a query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of results to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "param">The parameters to pass, if any.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<IEnumerable<Person>> QueryAsync<T>(this DbConnection connection, string sql, object param, CancellationToken cancellationToken = default) where T : Person { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = sql; foreach (var property in param.GetType().GetProperties()) { var parameter = command.CreateParameter(); parameter.ParameterName = "@" + property.Name; parameter.Value = property.GetValue(param); command.Parameters.Add(parameter); } await using var reader = await command.ExecuteReaderAsync(cancellationToken: cancellationToken); var persons = new Dictionary<int, Person>(); try { while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { Person person = null; var person1Id = reader.IsDBNull(0) ? default : reader.GetInt32(0); if (!persons.TryGetValue(person1Id, out person)) { person = new Person() { ID = person1Id, FirstName = reader.IsDBNull(1) ? default : reader.GetString(1), LastName = reader.IsDBNull(2) ? default : reader.GetString(2), }; persons.Add(person1Id, person); } } return persons.Values; } finally { reader.Close(); if (wasClosed) connection.Close(); } } /// <summary> /// Execute a query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of results to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "cacheableQuery">The cache that you need to impl, if you want to be faster.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<IEnumerable<Person>> QueryAsync<T>(this DbConnection connection, string sql, ICacheableQuery<Person> cacheableQuery, CancellationToken cancellationToken = default) where T : Person { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = sql; var identityQuery = new IdentityQuery(sql); var cacheableResults = await cacheableQuery.GetCacheableResultsAsync(identityQuery); if (cacheableResults.Any()) return cacheableResults; await using var reader = await command.ExecuteReaderAsync(cancellationToken: cancellationToken); var persons = new Dictionary<int, Person>(); try { while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { Person person = null; var person1Id = reader.IsDBNull(0) ? default : reader.GetInt32(0); if (!persons.TryGetValue(person1Id, out person)) { person = new Person() { ID = person1Id, FirstName = reader.IsDBNull(1) ? default : reader.GetString(1), LastName = reader.IsDBNull(2) ? default : reader.GetString(2), }; persons.Add(person1Id, person); } } await cacheableQuery.SetCacheableResultsAsync(identityQuery, persons.Values); return persons.Values; } finally { reader.Close(); if (wasClosed) connection.Close(); } } /// <summary> /// Execute a query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of results to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "param">The parameters to pass, if any.</param> /// <param name = "cacheableQuery">The cache that you need to impl, if you want to be faster.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<IEnumerable<Person>> QueryAsync<T>(this DbConnection connection, string sql, object param, ICacheableQuery<Person> cacheableQuery, CancellationToken cancellationToken = default) where T : Person { bool wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) await connection.OpenAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = sql; foreach (var property in param.GetType().GetProperties()) { var parameter = command.CreateParameter(); parameter.ParameterName = "@" + property.Name; parameter.Value = property.GetValue(param); command.Parameters.Add(parameter); } var identityQuery = new IdentityQuery(sql); var cacheableResults = await cacheableQuery.GetCacheableResultsAsync(identityQuery); if (cacheableResults.Any()) return cacheableResults; await using var reader = await command.ExecuteReaderAsync(cancellationToken: cancellationToken); var persons = new Dictionary<int, Person>(); try { while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { Person person = null; var person1Id = reader.IsDBNull(0) ? default : reader.GetInt32(0); if (!persons.TryGetValue(person1Id, out person)) { person = new Person() { ID = person1Id, FirstName = reader.IsDBNull(1) ? default : reader.GetString(1), LastName = reader.IsDBNull(2) ? default : reader.GetString(2), }; persons.Add(person1Id, person); } } await cacheableQuery.SetCacheableResultsAsync(identityQuery, persons.Values); return persons.Values; } finally { reader.Close(); if (wasClosed) connection.Close(); } } /// <summary> /// Execute a single-row query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of result to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A first sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<Person?> QueryFirstOrDefaultAsync<T>(this DbConnection connection, string sql, CancellationToken cancellationToken = default) where T : Person { return (await connection.QueryAsync<Person>(sql, cancellationToken)).FirstOrDefault(); } /// <summary> /// Execute a single-row query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of result to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "param">The parameters to pass, if any.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A first sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<Person?> QueryFirstOrDefaultAsync<T>(this DbConnection connection, string sql, object param, CancellationToken cancellationToken = default) where T : Person { return (await connection.QueryAsync<Person>(sql, param, cancellationToken)).FirstOrDefault(); } /// <summary> /// Execute a single-row query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of result to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "cacheableQuery">The cache that you need to impl, if you want to be faster.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A first sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<Person?> QueryFirstOrDefaultAsync<T>(this DbConnection connection, string sql, ICacheableQuery<Person> cacheableQuery, CancellationToken cancellationToken = default) where T : Person { return (await connection.QueryAsync<Person>(sql, cacheableQuery, cancellationToken)).FirstOrDefault(); } /// <summary> /// Execute a single-row query asynchronously using Task. /// </summary> /// <typeparam name = "T">The type of result to return.</typeparam> /// <param name = "sql">The SQL to execute for the query.</param> /// <param name = "param">The parameters to pass, if any.</param> /// <param name = "cacheableQuery">The cache that you need to impl, if you want to be faster.</param> /// <param name = "cancellationToken">The cancellation token for this command.</param> /// <returns> /// A first sequence of data of <typeparamref name = "T"/>; /// </returns> public static async Task<Person?> QueryFirstOrDefaultAsync<T>(this DbConnection connection, string sql, object param, ICacheableQuery<Person> cacheableQuery, CancellationToken cancellationToken = default) where T : Person { return (await connection.QueryAsync<Person>(sql, param, cacheableQuery, cancellationToken)).FirstOrDefault(); } } }
// <auto-generated /> using System; namespace Breezy; [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public sealed class SplitOnAttribute : Attribute { public int[] Index { get; init; } public SplitOnAttribute(params int[] index) => Index = index ?? throw new ArgumentNullException("index not defined"); }
// <auto-generated /> using System; namespace Breezy; [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public sealed class TableAttribute : Attribute { public string Name { get; init; } public TableAttribute(string name) => Name = name ?? throw new ArgumentNullException(name); }
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Breezy
Leave a Reply