Skip to main content

Gedaq by Vyacheslav Brevnov

Nuget / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: Gedaq

ORM Gedaq is roslyn generator of methods for obtaining data from databases.

Author: Vyacheslav Brevnov

NuGet: https://www.nuget.org/packages/Gedaq/

You can find more details at https://github.com/SoftStoneDevelop/Gedaq

Source : https://github.com/SoftStoneDevelop/Gedaq

Original Readme

note

Gedaq

Nuget Downloads Stars License

Generator for obtaining and mapping data from the database. Generates methods (synchronous and/or asynchronous):

  • Creating a Command for a query

  • Method of setting parameters in a Command

  • Command execution, with data mapping

  • Creating a QueryBatch Command

  • Setting parameters in a QueryBatch Command

  • Get data from a batch Command

  • Receiving data from a batch of queries, with data mapping

  • Getting data based on a query, with data mapping

There are versions for all of these methods (if possible):

  • Query
  • NonQuery
  • ЕecuteScalar (return type is determined automatically at the generation stage)
    It also generates methods specific to each provider, such as BinaryImport and BinaryExport in PostgreSQL.

Supported databases(see examples and documentation in the relevant DB package):

Usage:

For example, we have a Person class:


public class Person
{
public int Id { get; set; }

public string FirstName { get; set; }

public string MiddleName { get; set; }

public string LastName { get; set; }

public Identification Identification { get; set; }
}

public class Identification
{
public int Id { get; set; }
public string TypeName { get; set; }
}

We just mark anywhere in the code with a special attribute (class, structure, method) that tells the analyzer to generate the code. Let's mark the Person class itself with an attribute:


[Query(
@"
SELECT
p.id,
p.firstname,
~StartInner::Identification:id~
i.id,
i.typename,
~EndInner::Identification~
p.middlename,
p.lastname
FROM person p
LEFT JOIN identification i ON i.id = p.identification_id
WHERE p.id > $1
",
"GetAllPerson",
typeof(Person),
MethodType.Sync | MethodType.Async
),
Parametr(parametrType: typeof(int), position: 1)
]
public class Person
//...

Now in the code we can call the ready method:


var persons =
connection
.GetAllPerson(49999)
.ToList();

var personsAsync =
await connection
.GetAllPersonAsync(49999)
.ToListAsync();

Comparison with Dapper and DapperAOT of getting 50000 Person in a loop(Size is number of loop iterations) from the database:

.NET 7 Benchmark:

MethodSizeMeanRatioAllocatedAlloc Ratio
Gedaq.Npgsql10445.5 ms1.00132.09 MB1.00
Dapper10749.2 ms1.68150.41 MB1.14
DapperAOT10777.5 ms1.75150.4 MB1.14
Gedaq.Npgsql20901.9 ms1.00264.17 MB1.00
Dapper201,510.0 ms1.68300.81 MB1.14
DapperAOT201,505.3 ms1.67300.81 MB1.14
Gedaq.Npgsql301,366.2 ms1.00396.28 MB1.00
Dapper302,276.7 ms1.67451.22 MB1.14
DapperAOT302,279.6 ms1.67451.22 MB1.14

But with Gedaq, we can prepare the command in advance.


var personsCmd = connection.CreateGetAllPersonCommand(prepare: true);
personsCmd.SetGetAllPersonParametrs(49999);
var persons = personsCmd.ExecuteGetAllPersonCommand().ToList();

//or

var personsCmd = await connection.CreateGetAllPersonCommandAsync(prepare: true);
personsCmd.SetGetAllPersonParametrs(49999);
var persons = await personsCmd.ExecuteGetAllPersonCommandAsync().ToListAsync();


About

note

Generating code from attribute query

How to use

Example ( source csproj, source files )

This is the CSharp Project that references Gedaq

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Gedaq" Version="1.5.0" OutputItemType="Analyzer" ReferenceOutputAssembly="True" />
<PackageReference Include="Gedaq.Npgsql" Version="1.2.6" />
</ItemGroup>
</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX


using Npgsql;
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace GedaqDemoConsole.Example2
{
public partial class PersonRepository2
{

public IEnumerable<IEnumerable<GedaqDemoConsole.Model.Person>> BatchPersons(
Npgsql.NpgsqlConnection connection,
System.Int32 person_idBatch1,
System.Int32 person_idBatch2,
int? timeout = null
)
{
bool needClose = connection.State == ConnectionState.Closed;
if(needClose)
{
connection.Open();
}
NpgsqlBatch batch = null;
NpgsqlDataReader reader = null;
try
{
batch =
CreateBatchPersonsBatch(connection
, false)
;
SetBatchPersonsParametrs(
batch,
person_idBatch1,
person_idBatch2,
timeout
);
reader = batch.ExecuteReader();
yield return BatchItem0(reader);
reader.NextResult();
yield return BatchItem1(reader);
reader.NextResult();
reader.Dispose();
reader = null;
}
finally
{
if (reader != null)
{
if (!reader.IsClosed)
{
try
{
batch.Cancel();
}
catch { /* ignore */ }
}

reader.Dispose();
}
if (needClose)
{
connection.Close();
}
if(batch != null)
{
batch.BatchCommands.Clear();
batch.Dispose();
}
}
}

public async IAsyncEnumerable<IAsyncEnumerable<GedaqDemoConsole.Model.Person>> BatchPersonsAsync(
Npgsql.NpgsqlConnection connection,
System.Int32 person_idBatch1,
System.Int32 person_idBatch2,
int? timeout = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
bool needClose = connection.State == ConnectionState.Closed;
if(needClose)
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
}
NpgsqlBatch batch = null;
NpgsqlDataReader reader = null;
try
{
batch =
await CreateBatchPersonsBatchAsync(connection
, false, cancellationToken)
;
SetBatchPersonsParametrs(
batch,
person_idBatch1,
person_idBatch2,
timeout
);
reader = await batch.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
yield return BatchItem0Async(reader, cancellationToken);
await reader.NextResultAsync(cancellationToken).ConfigureAwait(false);
yield return BatchItem1Async(reader, cancellationToken);
await reader.NextResultAsync(cancellationToken).ConfigureAwait(false);
await reader.DisposeAsync().ConfigureAwait(false);
reader = null;
}
finally
{
if (reader != null)
{
if (!reader.IsClosed)
{
try
{
batch.Cancel();
}
catch { /* ignore */ }
}

await reader.DisposeAsync().ConfigureAwait(false);
}
if (needClose)
{
await connection.CloseAsync().ConfigureAwait(false);
}
if(batch != null)
{
batch.BatchCommands.Clear();
await batch.DisposeAsync().ConfigureAwait(false);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static async IAsyncEnumerable<GedaqDemoConsole.Model.Person> BatchItem0Async(
NpgsqlDataReader reader,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
while(await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
var item = new GedaqDemoConsole.Model.Person();
if(!reader.IsDBNull(0))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.Id = reader.GetFieldValue<System.Int32>(0);
}
if(!reader.IsDBNull(1))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.FirstName = reader.GetFieldValue<System.String>(1);
}
if(!reader.IsDBNull(2))
{
var item1 = new GedaqDemoConsole.Model.Address();
if(!reader.IsDBNull(2))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Id = reader.GetFieldValue<System.Int32>(2);
}
if(!reader.IsDBNull(3))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Street = reader.GetFieldValue<System.String>(3);
}
if(!reader.IsDBNull(4))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.City = reader.GetFieldValue<System.String>(4);
}
item.Address = item1;
}
yield return item;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static async IAsyncEnumerable<GedaqDemoConsole.Model.Person> BatchItem1Async(
NpgsqlDataReader reader,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
while(await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
var item = new GedaqDemoConsole.Model.Person();
if(!reader.IsDBNull(0))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.Id = reader.GetFieldValue<System.Int32>(0);
}
if(!reader.IsDBNull(1))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.FirstName = reader.GetFieldValue<System.String>(1);
}
if(!reader.IsDBNull(2))
{
var item1 = new GedaqDemoConsole.Model.Address();
if(!reader.IsDBNull(2))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Id = reader.GetFieldValue<System.Int32>(2);
}
if(!reader.IsDBNull(3))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Street = reader.GetFieldValue<System.String>(3);
}
if(!reader.IsDBNull(4))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.City = reader.GetFieldValue<System.String>(4);
}
item.Address = item1;
}
yield return item;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IEnumerable<GedaqDemoConsole.Model.Person> BatchItem0(NpgsqlDataReader reader)
{
while(reader.Read())
{
var item = new GedaqDemoConsole.Model.Person();
if(!reader.IsDBNull(0))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.Id = reader.GetFieldValue<System.Int32>(0);
}
if(!reader.IsDBNull(1))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.FirstName = reader.GetFieldValue<System.String>(1);
}
if(!reader.IsDBNull(2))
{
var item1 = new GedaqDemoConsole.Model.Address();
if(!reader.IsDBNull(2))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Id = reader.GetFieldValue<System.Int32>(2);
}
if(!reader.IsDBNull(3))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Street = reader.GetFieldValue<System.String>(3);
}
if(!reader.IsDBNull(4))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.City = reader.GetFieldValue<System.String>(4);
}
item.Address = item1;
}
yield return item;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IEnumerable<GedaqDemoConsole.Model.Person> BatchItem1(NpgsqlDataReader reader)
{
while(reader.Read())
{
var item = new GedaqDemoConsole.Model.Person();
if(!reader.IsDBNull(0))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.Id = reader.GetFieldValue<System.Int32>(0);
}
if(!reader.IsDBNull(1))
{
if(item == null)
{
item = new GedaqDemoConsole.Model.Person();
}
item.FirstName = reader.GetFieldValue<System.String>(1);
}
if(!reader.IsDBNull(2))
{
var item1 = new GedaqDemoConsole.Model.Address();
if(!reader.IsDBNull(2))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Id = reader.GetFieldValue<System.Int32>(2);
}
if(!reader.IsDBNull(3))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.Street = reader.GetFieldValue<System.String>(3);
}
if(!reader.IsDBNull(4))
{
if(item1 == null)
{
item1 = new GedaqDemoConsole.Model.Address();
}
item1.City = reader.GetFieldValue<System.String>(4);
}
item.Address = item1;
}
yield return item;
}
}

public NpgsqlBatch CreateBatchPersonsBatch(
Npgsql.NpgsqlConnection connection,
bool prepare = false
)
{
var batch = connection.CreateBatch();
var command = batch.CreateBatchCommand();
command.CommandText = @"

SELECT
p.id,
p.firstname,

a.id,
a.street,
a.city

FROM person p
LEFT JOIN address a ON a.id = p.address_id
WHERE
p.id = $1

";
{
{
var parametr = new NpgsqlParameter<System.Int32>();

command.Parameters.Add(parametr);

}
}
batch.BatchCommands.Add(command);
command = batch.CreateBatchCommand();
command.CommandText = @"

SELECT
p.id,
p.firstname,

a.id,
a.street,
a.city

FROM person p
LEFT JOIN address a ON a.id = p.address_id
WHERE
p.id = $1

";
{
{
var parametr = new NpgsqlParameter<System.Int32>();

command.Parameters.Add(parametr);

}
}
batch.BatchCommands.Add(command);
if(prepare)
{
try
{
batch.Prepare();
}
catch
{
batch.Dispose();
throw;
}
}
return batch;
}

public async ValueTask<NpgsqlBatch> CreateBatchPersonsBatchAsync(
Npgsql.NpgsqlConnection connection,
bool prepare = false,
CancellationToken cancellationToken = default
)
{
var batch = connection.CreateBatch();
var command = batch.CreateBatchCommand();
command.CommandText = @"

SELECT
p.id,
p.firstname,

a.id,
a.street,
a.city

FROM person p
LEFT JOIN address a ON a.id = p.address_id
WHERE
p.id = $1

";
{
{
var parametr = new NpgsqlParameter<System.Int32>();

command.Parameters.Add(parametr);

}
}
batch.BatchCommands.Add(command);
command = batch.CreateBatchCommand();
command.CommandText = @"

SELECT
p.id,
p.firstname,

a.id,
a.street,
a.city

FROM person p
LEFT JOIN address a ON a.id = p.address_id
WHERE
p.id = $1

";
{
{
var parametr = new NpgsqlParameter<System.Int32>();

command.Parameters.Add(parametr);

}
}
batch.BatchCommands.Add(command);
if(prepare)
{
try
{
await batch.PrepareAsync(cancellationToken).ConfigureAwait(false);
}
catch
{
await batch.DisposeAsync().ConfigureAwait(false);
throw;
}
}
return batch;
}

public void SetBatchPersonsParametrs(
NpgsqlBatch batch,
System.Int32 person_idBatch1,
System.Int32 person_idBatch2,
int? timeout = null
)
{

if(timeout.HasValue)
{
batch.Timeout = timeout.Value;
}
var batchCommand = batch.BatchCommands[0];
((NpgsqlParameter<System.Int32>)batchCommand.Parameters[0]).TypedValue = person_idBatch1;
batchCommand = batch.BatchCommands[1];
((NpgsqlParameter<System.Int32>)batchCommand.Parameters[0]).TypedValue = person_idBatch2;
}

public IEnumerable<IEnumerable<GedaqDemoConsole.Model.Person>> ExecuteBatchPersonsBatch(
NpgsqlBatch batch
)
{
NpgsqlDataReader reader = null;
try
{
reader = batch.ExecuteReader();
yield return BatchItem0(reader);
reader.NextResult();
yield return BatchItem1(reader);
reader.NextResult();
reader.Dispose();
reader = null;
}
finally
{
if (reader != null)
{
if (!reader.IsClosed)
{
try
{
batch.Cancel();
}
catch { /* ignore */ }
}

reader.Dispose();
}
}
}

public async IAsyncEnumerable<IAsyncEnumerable<GedaqDemoConsole.Model.Person>> ExecuteBatchPersonsBatchAsync(
NpgsqlBatch batch,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
NpgsqlDataReader reader = null;
try
{
reader = await batch.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
yield return BatchItem0Async(reader, cancellationToken);
await reader.NextResultAsync(cancellationToken).ConfigureAwait(false);
yield return BatchItem1Async(reader, cancellationToken);
await reader.NextResultAsync(cancellationToken).ConfigureAwait(false);
await reader.DisposeAsync().ConfigureAwait(false);
reader = null;
}
finally
{
if (reader != null)
{
if (!reader.IsClosed)
{
try
{
batch.Cancel();
}
catch { /* ignore */ }
}

await reader.DisposeAsync().ConfigureAwait(false);
}
}
}

}
}