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