Add project files.

This commit is contained in:
Sergio Matias Urquin
2025-04-29 18:42:29 -06:00
parent 9c1958d351
commit 83fc1878c4
67 changed files with 4586 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace Core.Blueprint.SQLServer.Entities
{
/// <summary>
/// Represents the base class for SQL Server entities, providing common properties for auditing and state management.
/// </summary>
public abstract class BaseSQLAdapter : IBaseSQLAdapter
{
/// <summary>
/// Gets or sets the identifier for the entity.
/// </summary>
[Key]
[JsonPropertyName("id")]
public int Id { get; init; }
/// <summary>
/// Gets or sets the unique identifier for the entity.
/// </summary>
[JsonPropertyName("guid")]
public string Guid { get; init; }
/// <summary>
/// Gets or sets the timestamp when the entity was created.
/// Default value is the current UTC time at the moment of instantiation.
/// </summary>
[JsonPropertyName("createdAt")]
public DateTime? CreatedAt { get; init; }
/// <summary>
/// Gets or sets the identifier of the user or system that created the entity.
/// </summary>
[JsonPropertyName("createdBy")]
public string? CreatedBy { get; set; }
/// <summary>
/// Gets or sets the timestamp when the entity was last updated.
/// Null if the entity has not been updated.
/// </summary>
[JsonPropertyName("updatedAt")]
public DateTime? UpdatedAt { get; set; }
/// <summary>
/// Gets or sets the identifier of the user or system that last updated the entity.
/// Null if the entity has not been updated.
/// </summary>
[JsonPropertyName("updatedBy")]
public string? UpdatedBy { get; set; }
/// <summary>
/// Gets or sets the status of the entity, indicating whether it is active, inactive, or in another state.
/// Default value is <see cref="StatusEnum.Active"/>.
/// </summary>
[JsonPropertyName("status")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public StatusEnum Status { get; set; }
protected BaseSQLAdapter()
{
Guid = System.Guid.NewGuid().ToString();
CreatedAt = DateTime.UtcNow;
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Text.Json.Serialization;
namespace Core.Blueprint.SQLServer.Entities
{
/// <summary>
/// Defines the possible statuses for entities in the system.
/// Used to track the state of an entity, such as whether it is active, inactive, or deleted.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum StatusEnum
{
/// <summary>
/// Indicates that the entity is currently active and operational.
/// </summary>
Active = 0,
/// <summary>
/// Indicates that the entity is currently inactive but still exists in the system.
/// Typically used for temporary deactivation or soft-offline states.
/// </summary>
Inactive = 1,
/// <summary>
/// Indicates that the entity has been deleted and is no longer accessible.
/// Often used in soft-delete scenarios where the entity is retained for archival or audit purposes.
/// </summary>
Deleted = 2
}
}

View File

@@ -0,0 +1,33 @@
using Azure.Identity;
using Core.Blueprint.DAL.SQLServer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Core.Blueprint.SQLServer.Configuration
{
/// <summary>
/// Provides extension methods for configuring SQL Server.
/// </summary>
public static class RegisterBlueprint
{
/// <summary>
/// Configures SQL Server services, including the database context and generic repository, for dependency injection.
/// </summary>
/// <param name="services">The service collection to which the SQL Server services will be added.</param>
/// <param name="configuration">The application configuration object for accessing settings such as connection strings.</param>
/// <returns>An updated <see cref="IServiceCollection"/> with SQL Server services registered.</returns>
public static IServiceCollection AddSQLServer(this IServiceCollection services, IConfiguration configuration)
{
var chainedCredentials = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new SharedTokenCacheCredential(),
new VisualStudioCredential(),
new VisualStudioCodeCredential()
);
services.AddScoped(typeof(IEntityRepository<,>), typeof(EntityRepository<,>));
return services;
}
}
}

View File

@@ -0,0 +1,57 @@
using Core.Blueprint.SQLServer.Entities;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace Core.Blueprint.SQLServer
{
/// <summary>
/// Defines the interface for SQL Server entities, providing common properties for auditing and state management.
/// </summary>
public interface IBaseSQLAdapter
{
/// <summary>
/// Gets or sets the identifier for the entity.
/// </summary>
[Key]
[JsonPropertyName("id")]
int Id { get; }
/// <summary>
/// Gets or sets the GUID for the entity.
/// </summary>
[JsonPropertyName("guid")]
string Guid { get; }
/// <summary>
/// Gets or sets the timestamp when the entity was created.
/// </summary>
[JsonPropertyName("createdAt")]
DateTime? CreatedAt { get; }
/// <summary>
/// Gets or sets the identifier of the user or system that created the entity.
/// </summary>
[JsonPropertyName("createdBy")]
string? CreatedBy { get; set; }
/// <summary>
/// Gets or sets the timestamp when the entity was last updated.
/// </summary>
[JsonPropertyName("updatedAt")]
DateTime? UpdatedAt { get; set; }
/// <summary>
/// Gets or sets the identifier of the user or system that last updated the entity.
/// </summary>
[JsonPropertyName("updatedBy")]
string? UpdatedBy { get; set; }
/// <summary>
/// Gets or sets the status of the entity, indicating whether it is active, inactive, or in another state.
/// </summary>
[JsonPropertyName("status")]
[JsonConverter(typeof(JsonStringEnumConverter))]
StatusEnum Status { get; set; }
}
}

View File

@@ -0,0 +1,108 @@
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
namespace Core.Blueprint.DAL.SQLServer
{
/// <summary>
/// Defines the contract for a generic repository to manage entities in a SQL Server database.
/// </summary>
/// <typeparam name="TEntity">The type of the entity managed by the repository. Must be a class.</typeparam>
/// <typeparam name="TContext">The type of the database context used by the repository. Must inherit from <see cref="DbContext"/>.</typeparam>
public interface IEntityRepository<TEntity, TContext>
where TEntity : class
where TContext : DbContext
{
/// <summary>
/// Retrieves all entities of type <typeparamref name="T"/> from the database.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a collection of entities as the result.</returns>
Task<IEnumerable<TEntity>> GetAllAsync();
/// <summary>
/// Retrieves all entities of type <typeparamref name="T"/> from the database that match a specified condition.
/// </summary>
/// <param name="predicate">An expression to filter the entities.</param>
/// <returns>A task representing the asynchronous operation, with a collection of matching entities as the result.</returns>
Task<IEnumerable<TEntity>> GetByConditionAsync(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Retrieves a single entity of type <typeparamref name="T"/> by its identifier.
/// </summary>
/// <param name="id">The identifier of the entity to retrieve.</param>
/// <returns>A task representing the asynchronous operation, with the entity as the result, or null if not found.</returns>
Task<TEntity?> GetByIdAsync(int id);
/// <summary>
/// Retrieves the first entity of type <typeparamref name="T"/> that matches a specified condition, or null if no match is found.
/// </summary>
/// <param name="predicate">An expression to filter the entities.</param>
/// <returns>A task representing the asynchronous operation, with the matching entity as the result, or null if none match.</returns>
Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Adds a new entity of type <typeparamref name="T"/> to the database.
/// </summary>
/// <param name="entity">The entity to add.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task AddAsync(TEntity entity);
/// <summary>
/// Adds multiple entities of type <typeparamref name="T"/> to the database.
/// </summary>
/// <param name="entities">The collection of entities to add.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task AddRangeAsync(IEnumerable<TEntity> entities);
/// <summary>
/// Updates an existing entity of type <typeparamref name="T"/> in the database.
/// </summary>
/// <param name="entity">The entity to update.</param>
/// <returns>The updated entity.</returns>
TEntity Update(TEntity entity);
/// <summary>
/// Updates multiple entities of type <typeparamref name="T"/> in the database.
/// </summary>
/// <param name="entities">The collection of entities to update.</param>
void UpdateRange(IEnumerable<TEntity> entities);
/// <summary>
/// Deletes an entity of type <typeparamref name="T"/> from the database.
/// </summary>
/// <param name="entity">The entity to delete.</param>
void Delete(TEntity entity);
/// <summary>
/// Deletes multiple entities of type <typeparamref name="T"/> from the database.
/// </summary>
/// <param name="entities">The collection of entities to delete.</param>
void DeleteRange(IEnumerable<TEntity> entities);
/// <summary>
/// Determines whether any entities of type <typeparamref name="T"/> exist in the database that match a specified condition.
/// </summary>
/// <param name="predicate">An expression to filter the entities.</param>
/// <returns>A task representing the asynchronous operation, with a boolean result indicating whether any match exists.</returns>
Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Executes a raw SQL query and maps the result to entities of type <typeparamref name="T"/>.
/// </summary>
/// <param name="sql">The raw SQL query to execute.</param>
/// <param name="parameters">Optional parameters for the SQL query.</param>
/// <returns>A task representing the asynchronous operation, with a collection of entities as the result.</returns>
Task<IEnumerable<TEntity>> ExecuteRawSqlAsync(string sql, params object[] parameters);
/// <summary>
/// Counts the total number of entities of type <typeparamref name="T"/> in the database.
/// </summary>
/// <returns>A task representing the asynchronous operation, with the count as the result.</returns>
Task<int> CountAsync();
/// <summary>
/// Saves all pending changes to the database.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
Task SaveAsync();
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.13.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,182 @@
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
namespace Core.Blueprint.DAL.SQLServer
{
/// <summary>
/// The <see cref="EntityRepository{TEntity, TContext}"/> class provides a comprehensive generic repository
/// for managing entities using Entity Framework Core with SQL Server as the underlying database.
/// Designed as a package for consumption by external applications.
/// </summary>
/// <typeparam name="TEntity">The entity type managed by the repository. Must be a class.</typeparam>
/// <typeparam name="TContext">The database context type. Must inherit from <see cref="DbContext"/>.</typeparam>
public class EntityRepository<TEntity, TContext> : IEntityRepository<TEntity, TContext>
where TEntity : class
where TContext : DbContext
{
private readonly TContext _context;
private readonly DbSet<TEntity> _dbSet;
/// <summary>
/// Initializes a new instance of the <see cref="EntityRepository{TEntity, TContext}"/> class with a specified database context.
/// </summary>
/// <param name="context">The <see cref="TContext"/> for database operations.</param>
public EntityRepository(TContext context)
{
_context = context;
_dbSet = _context.Set<TEntity>();
}
/// <summary>
/// Retrieves all entities of type <typeparamref name="TEntity"/> from the database.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a list of entities as the result.</returns>
public async Task<IEnumerable<TEntity>> GetAllAsync()
{
return await _dbSet.ToListAsync();
}
/// <summary>
/// Retrieves all entities of type <typeparamref name="TEntity"/> from the database that match a specified filter.
/// </summary>
/// <param name="predicate">An expression to filter entities.</param>
/// <returns>A task representing the asynchronous operation, with a list of filtered entities as the result.</returns>
public async Task<IEnumerable<TEntity>> GetByConditionAsync(Expression<Func<TEntity, bool>> predicate)
{
return await _dbSet.Where(predicate).ToListAsync();
}
/// <summary>
/// Retrieves a single entity of type <typeparamref name="TEntity"/> by its identifier.
/// </summary>
/// <param name="id">The identifier of the entity.</param>
/// <returns>A task representing the asynchronous operation, with the entity as the result, or null if not found.</returns>
public async Task<TEntity?> GetByIdAsync(int id)
{
var existingEntity = await _dbSet.FindAsync(id);
if (existingEntity != null)
{
_context.Entry(existingEntity).State = EntityState.Detached;
}
return existingEntity;
}
/// <summary>
/// Adds a new entity to the database.
/// </summary>
/// <param name="entity">The entity to add.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task AddAsync(TEntity entity)
{
await _dbSet.AddAsync(entity);
}
/// <summary>
/// Adds multiple entities to the database.
/// </summary>
/// <param name="entities">The collection of entities to add.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task AddRangeAsync(IEnumerable<TEntity> entities)
{
await _dbSet.AddRangeAsync(entities);
}
/// <summary>
/// Updates an existing entity in the database.
/// </summary>
/// <param name="entity">The entity to update.</param>
/// <returns>The updated entity.</returns>
public TEntity Update(TEntity entity)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
/// <summary>
/// Updates multiple entities in the database.
/// </summary>
/// <param name="entities">The collection of entities to update.</param>
public void UpdateRange(IEnumerable<TEntity> entities)
{
foreach (var entity in entities)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
/// <summary>
/// Deletes an entity from the database.
/// </summary>
/// <param name="entity">The entity to delete.</param>
public void Delete(TEntity entity)
{
if (_context.Entry(entity).State == EntityState.Detached)
{
_dbSet.Attach(entity);
}
_dbSet.Remove(entity);
}
/// <summary>
/// Deletes multiple entities from the database.
/// </summary>
/// <param name="entities">The collection of entities to delete.</param>
public void DeleteRange(IEnumerable<TEntity> entities)
{
_dbSet.RemoveRange(entities);
}
/// <summary>
/// Retrieves the first entity matching the specified condition or null if no match is found.
/// </summary>
/// <param name="predicate">An expression to filter entities.</param>
/// <returns>A task representing the asynchronous operation, with the matched entity as the result.</returns>
public async Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
return await _dbSet.FirstOrDefaultAsync(predicate);
}
/// <summary>
/// Determines if any entities exist that match the specified condition.
/// </summary>
/// <param name="predicate">An expression to filter entities.</param>
/// <returns>A task representing the asynchronous operation, with a boolean result indicating existence.</returns>
public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate)
{
return await _dbSet.AnyAsync(predicate);
}
/// <summary>
/// Saves all pending changes to the database.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task SaveAsync()
{
await _context.SaveChangesAsync();
}
/// <summary>
/// Executes a raw SQL query and maps the result to the specified entity type.
/// </summary>
/// <param name="sql">The raw SQL query.</param>
/// <param name="parameters">Optional parameters for the query.</param>
/// <returns>An <see cref="IEnumerable{TEntity}"/> representing the result set.</returns>
public async Task<IEnumerable<TEntity>> ExecuteRawSqlAsync(string sql, params object[] parameters)
{
return await _dbSet.FromSqlRaw(sql, parameters).ToListAsync();
}
/// <summary>
/// Counts the total number of entities in the database.
/// </summary>
/// <returns>A task representing the asynchronous operation, with the count as the result.</returns>
public async Task<int> CountAsync()
{
return await _dbSet.CountAsync();
}
}
}