Compare commits

...

6 Commits

Author SHA1 Message Date
Developer 02
1e54b775a2 refactor: Vereinfachung der SQLExecutor-Implementierung zur Rückgabe von IQueryExecutor
- Aktualisiert `SQLExecutor<T>` um `ISQLExecutor<T>` zu implementieren und `IQueryExecutor<T>` für die weitere Abfrageausführung zurückzugeben.
- Umstrukturierte Methoden zur Verwendung von `ToExecutor()` für die Konvertierung von rohen SQL-Abfragen in einen `IQueryExecutor<T>`.
- Geänderter Konstruktor, um `IServiceProvider` für die Injektion von Abhängigkeiten von benutzerdefinierten SQL-Abfrageklassen zu akzeptieren.
2025-04-29 17:14:38 +02:00
Developer 02
6e82b24578 feat: Implement QueryExecutor for executing queries using IQueryable
- Added a record `QueryExecutor<TEntity>` implementing `IQueryExecutor<TEntity>` for executing common query operations like First, Single, and ToList both synchronously and asynchronously.
- Methods leverage the IQueryable interface to perform database queries for entity types.
2025-04-29 16:56:13 +02:00
Developer 02
5331efe3c1 feat(sql): Hinzufügen generischer Überladungen zu SQLExecutor unter Verwendung von ISQL<T> über DI
Es wurden Überladungen für ExecuteFirstAsync, ExecuteSingleAsync und ExecuteAllAsync
eingeführt, die SQL-Definitionen aus dem Dependency Injection Container mittels ISQL<T> auflösen.
Außerdem wurde ein Fehler bei der Verwendung der Direktive von .cs zu standardmäßigem Schnittstellenimport korrigiert.
2025-04-29 16:39:18 +02:00
Developer 02
3b4ad2960a feat: SQLExecutor-Klasse für die Ausführung von Roh-SQL-Abfragen mit Entity Framework Core hinzufügen 2025-04-29 16:26:26 +02:00
Developer 02
33048e185b feat(CreateEnvelopeReceiverCommandHandler): initialized 2025-04-29 14:41:21 +02:00
Developer 02
3ae1b94eb7 refactor(Login): Id in UserId umbenennen 2025-04-29 11:39:21 +02:00
10 changed files with 229 additions and 3 deletions

View File

@@ -0,0 +1,70 @@
namespace EnvelopeGenerator.Application.Contracts.SQLExecutor;
/// <summary>
/// Provides methods for executing common Entity Framework queries on a given entity type.
/// This interface abstracts away the direct usage of Entity Framework methods for querying data
/// and provides asynchronous and synchronous operations for querying a collection or single entity.
/// </summary>
/// <typeparam name="TEntity">The type of the entity being queried.</typeparam>
public interface IQueryExecutor<TEntity>
{
/// <summary>
/// Asynchronously retrieves the first entity or a default value if no entity is found.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
public Task<TEntity?> FirstOrDefaultAsync();
/// <summary>
/// Asynchronously retrieves a single entity or a default value if no entity is found.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
public Task<TEntity?> SingleOrDefaultAsync();
/// <summary>
/// Asynchronously retrieves a list of entities.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the list of entities.</returns>
public Task<IEnumerable<TEntity>> ToListAsync();
/// <summary>
/// Asynchronously retrieves the first entity. Throws an exception if no entity is found.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the first entity.</returns>
public Task<TEntity> FirstAsync();
/// <summary>
/// Asynchronously retrieves a single entity. Throws an exception if no entity is found.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the single entity.</returns>
public Task<TEntity> SingleAsync();
/// <summary>
/// Synchronously retrieves the first entity or a default value if no entity is found.
/// </summary>
/// <returns>The first entity or a default value.</returns>
public TEntity? FirstOrDefault();
/// <summary>
/// Synchronously retrieves a single entity or a default value if no entity is found.
/// </summary>
/// <returns>The single entity or a default value.</returns>
public TEntity? SingleOrDefault();
/// <summary>
/// Synchronously retrieves a list of entities.
/// </summary>
/// <returns>The list of entities.</returns>
public IEnumerable<TEntity> ToList();
/// <summary>
/// Synchronously retrieves the first entity. Throws an exception if no entity is found.
/// </summary>
/// <returns>The first entity.</returns>
public TEntity First();
/// <summary>
/// Synchronously retrieves a single entity. Throws an exception if no entity is found.
/// </summary>
/// <returns>The single entity.</returns>
public TEntity Single();
}

View File

@@ -0,0 +1,20 @@
namespace EnvelopeGenerator.Application.Contracts.SQLExecutor;
/// <summary>
/// Represents a raw SQL query contract.
/// </summary>
public interface ISQL
{
/// <summary>
/// Gets the raw SQL query string.
/// </summary>
string Raw { get; }
}
/// <summary>
/// Represents a typed SQL query contract for a specific entity.
/// </summary>
/// <typeparam name="TEntity">The type of the entity associated with the SQL query.</typeparam>
public interface ISQL<TEntity> : ISQL
{
}

View File

@@ -0,0 +1,27 @@
namespace EnvelopeGenerator.Application.Contracts.SQLExecutor;
/// <summary>
/// Defines methods for executing raw SQL queries or custom SQL query classes and returning query executors for further operations.
/// Provides abstraction for raw SQL execution as well as mapping the results to <typeparamref name="TEntity"/> objects.
/// </summary>
/// <typeparam name="TEntity">The entity type to which the SQL query results will be mapped.</typeparam>
public interface ISQLExecutor<TEntity>
{
/// <summary>
/// Executes a raw SQL query and returns an <see cref="IQueryExecutor{TEntity}"/> for further querying operations on the result.
/// </summary>
/// <param name="sql">The raw SQL query to execute.</param>
/// <param name="cancellation">Optional cancellation token for the operation.</param>
/// <param name="parameters">Optional parameters for the SQL query.</param>
/// <returns>An <see cref="IQueryExecutor{TEntity}"/> instance for further query operations on the result.</returns>
IQueryExecutor<TEntity> Execute(string sql, CancellationToken cancellation = default, params object[] parameters);
/// <summary>
/// Executes a custom SQL query defined by a class that implements <see cref="ISQL{TEntity}"/> and returns an <see cref="IQueryExecutor{TEntity}"/> for further querying operations on the result.
/// </summary>
/// <typeparam name="TSQL">The type of the custom SQL query class implementing <see cref="ISQL{TEntity}"/>.</typeparam>
/// <param name="cancellation">Optional cancellation token for the operation.</param>
/// <param name="parameters">Optional parameters for the SQL query.</param>
/// <returns>An <see cref="IQueryExecutor{TEntity}"/> instance for further query operations on the result.</returns>
IQueryExecutor<TEntity> Execute<TSQL>(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL<TEntity>;
}

View File

@@ -80,4 +80,8 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Procedures\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,24 @@
using MediatR;
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Commands.Create;
/// <summary>
/// Handles the creation of an envelope along with its associated document and recipients.
/// This command processes the envelope data, including title, message, document content,
/// recipient list, and optional two-factor authentication settings.
/// </summary>
public class CreateEnvelopeReceiverCommandHandler : IRequestHandler<CreateEnvelopeReceiverCommand>
{
/// <summary>
/// Handles the execution of the <see cref="CreateEnvelopeReceiverCommand"/>.
/// Responsible for validating input data, creating or retrieving recipients, associating signatures,
/// and storing the envelope and document details.
/// </summary>
/// <param name="request">The command containing all necessary information to create an envelope.</param>
/// <param name="cancellationToken">Token to observe while waiting for the task to complete.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public Task Handle(CreateEnvelopeReceiverCommand request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}

View File

@@ -6,8 +6,8 @@ namespace EnvelopeGenerator.GeneratorAPI.Models;
/// Repräsentiert ein Login-Modell mit erforderlichem Passwort und optionaler ID und Benutzername.
/// </summary>
/// <param name="Password">Das erforderliche Passwort für das Login.</param>
/// <param name="Id">Die optionale ID des Benutzers.</param>
/// <param name="UserId">Die optionale ID des Benutzers.</param>
/// <param name="Username">Der optionale Benutzername.</param>
public record Login([Required] string Password, int? Id = null, string? Username = null)
public record Login([Required] string Password, int? UserId = null, string? Username = null)
{
}

View File

@@ -22,7 +22,7 @@
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7174;http://localhost:5131",
"environmentVariables": {

View File

@@ -0,0 +1,42 @@
using AngleSharp.Dom;
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Infrastructure;
public sealed record QueryExecutor<TEntity> : IQueryExecutor<TEntity>
{
private readonly IQueryable<TEntity> _query;
internal QueryExecutor(IQueryable<TEntity> queryable)
{
_query = queryable;
}
public TEntity First() => _query.First();
public Task<TEntity> FirstAsync() => _query.FirstAsync();
public TEntity? FirstOrDefault() => _query.FirstOrDefault();
public Task<TEntity?> FirstOrDefaultAsync() => _query.FirstOrDefaultAsync();
public TEntity Single() => _query.Single();
public Task<TEntity> SingleAsync() => _query.SingleAsync();
public TEntity? SingleOrDefault() => _query.SingleOrDefault();
public Task<TEntity?> SingleOrDefaultAsync() => _query.SingleOrDefaultAsync();
public IEnumerable<TEntity> ToList() => _query.ToList();
public async Task<IEnumerable<TEntity>> ToListAsync() => await _query.ToListAsync();
}

View File

@@ -0,0 +1,9 @@
namespace EnvelopeGenerator.Infrastructure;
public static class QueryExecutorExtension
{
public static QueryExecutor<TEntity> ToExecutor<TEntity>(this IQueryable<TEntity> queryable) where TEntity : class
{
return new QueryExecutor<TEntity>(queryable);
}
}

View File

@@ -0,0 +1,30 @@
using EnvelopeGenerator.Application.Contracts.SQLExecutor;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace EnvelopeGenerator.Infrastructure;
public sealed class SQLExecutor<T> : ISQLExecutor<T> where T : class
{
private readonly EGDbContext _context;
private readonly IServiceProvider _provider;
public SQLExecutor(EGDbContext context, IServiceProvider provider)
{
_context = context;
_provider = provider;
}
public IQueryExecutor<T> Execute(string sql, CancellationToken cancellation = default, params object[] parameters)
=> _context
.Set<T>()
.FromSqlRaw(sql, parameters)
.ToExecutor();
public IQueryExecutor<T> Execute<TSQL>(CancellationToken cancellation = default, params object[] parameters) where TSQL : ISQL<T>
{
var sql = _provider.GetRequiredService<TSQL>();
return Execute(sql.Raw);
}
}