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