chore: initial git load of code space
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using Strata.SqlTools.Breakdowns.SqlServer;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for converting between QueryBreakdown and QueryBreakdownEntity for Entity Framework Core integration.
|
||||
/// </summary>
|
||||
public interface IQueryBreakdownMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdown (SQL Tools) to a QueryBreakdownEntity (EF Core).
|
||||
/// </summary>
|
||||
/// <param name="queryBreakdown">The QueryBreakdown to convert.</param>
|
||||
/// <returns>A QueryBreakdownEntity that can be persisted to the database.</returns>
|
||||
QueryBreakdownEntity MapToEntity(QueryBreakdown queryBreakdown);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdownEntity (EF Core) back to a QueryBreakdown (SQL Tools).
|
||||
/// </summary>
|
||||
/// <param name="entity">The QueryBreakdownEntity to convert.</param>
|
||||
/// <returns>A QueryBreakdown instance with all clauses and parameters restored.</returns>
|
||||
QueryBreakdown MapToDomainModel(QueryBreakdownEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdown to a QueryBreakdownEntity with related entities (parameters and WITH clauses).
|
||||
/// </summary>
|
||||
/// <param name="queryBreakdown">The QueryBreakdown to convert.</param>
|
||||
/// <returns>A QueryBreakdownEntity with all related entities populated.</returns>
|
||||
(QueryBreakdownEntity Entity, List<QueryParameterEntity> Parameters, List<WithClauseEntity> WithClauses) MapToEntityWithRelations(QueryBreakdown queryBreakdown);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdownEntity with related entities back to a QueryBreakdown.
|
||||
/// </summary>
|
||||
/// <param name="entity">The QueryBreakdownEntity with navigation properties loaded.</param>
|
||||
/// <returns>A fully reconstructed QueryBreakdown instance.</returns>
|
||||
QueryBreakdown MapToDomainModelWithRelations(QueryBreakdownEntity entity);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Entity Framework Core configuration for the QueryBreakdownEntity.
|
||||
/// Defines the table structure, relationships, and constraints.
|
||||
/// </summary>
|
||||
public class QueryBreakdownEntityConfiguration : IEntityTypeConfiguration<QueryBreakdownEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the QueryBreakdownEntity for Entity Framework Core.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<QueryBreakdownEntity> builder)
|
||||
{
|
||||
builder.ToTable("QueryBreakdowns");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
builder.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
|
||||
// Configure SELECT clause
|
||||
builder.Property(e => e.SelectClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.SelectClauseComment)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure FROM clause
|
||||
builder.Property(e => e.FromClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.FromClauseComment)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure WHERE clause
|
||||
builder.Property(e => e.WhereClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.WhereClauseComment)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure GROUP BY clause
|
||||
builder.Property(e => e.GroupByClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.GroupByClauseComment)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure HAVING clause
|
||||
builder.Property(e => e.HavingClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.HavingClauseComment)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure ORDER BY clause
|
||||
builder.Property(e => e.OrderByClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.OrderByClauseComment)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure WITH clause (CTEs)
|
||||
builder.Property(e => e.WithClause)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure raw SQL
|
||||
builder.Property(e => e.RawSql)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure JSON properties
|
||||
builder.Property(e => e.SetupClausesJson)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.FinishClausesJson)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.ParametersJson)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Configure timestamps
|
||||
builder.Property(e => e.CreatedAt)
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
builder.Property(e => e.UpdatedAt)
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
// Configure relationships
|
||||
builder.HasMany<QueryParameterEntity>()
|
||||
.WithOne(p => p.QueryBreakdownEntity)
|
||||
.HasForeignKey(p => p.QueryBreakdownEntityId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasMany<WithClauseEntity>()
|
||||
.WithOne(w => w.QueryBreakdownEntity)
|
||||
.HasForeignKey(w => w.QueryBreakdownEntityId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Create indexes for common queries
|
||||
builder.HasIndex(e => e.CreatedAt);
|
||||
builder.HasIndex(e => e.UpdatedAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Entity Framework Core configuration for the QueryParameterEntity.
|
||||
/// </summary>
|
||||
public class QueryParameterEntityConfiguration : IEntityTypeConfiguration<QueryParameterEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the QueryParameterEntity for Entity Framework Core.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<QueryParameterEntity> builder)
|
||||
{
|
||||
builder.ToTable("QueryParameters");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
builder.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
|
||||
builder.Property(e => e.QueryBreakdownEntityId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.ParameterName)
|
||||
.HasColumnType("nvarchar(256)")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.ParameterValue)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.ParameterTypeName)
|
||||
.HasColumnType("nvarchar(256)")
|
||||
.IsRequired(false);
|
||||
|
||||
// Create index for faster lookups
|
||||
builder.HasIndex(e => new { e.QueryBreakdownEntityId, e.ParameterName })
|
||||
.IsUnique();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Configurations;
|
||||
|
||||
/// <summary>
|
||||
/// Entity Framework Core configuration for the WithClauseEntity.
|
||||
/// </summary>
|
||||
public class WithClauseEntityConfiguration : IEntityTypeConfiguration<WithClauseEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the WithClauseEntity for Entity Framework Core.
|
||||
/// </summary>
|
||||
/// <param name="builder">The entity type builder.</param>
|
||||
public void Configure(EntityTypeBuilder<WithClauseEntity> builder)
|
||||
{
|
||||
builder.ToTable("WithClauses");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
builder.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
|
||||
builder.Property(e => e.QueryBreakdownEntityId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.CteName)
|
||||
.HasColumnType("nvarchar(256)")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.ColumnList)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired(false);
|
||||
|
||||
builder.Property(e => e.CteDefinition)
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.OrderIndex)
|
||||
.IsRequired();
|
||||
|
||||
// Create index for ordering and lookups
|
||||
builder.HasIndex(e => new { e.QueryBreakdownEntityId, e.OrderIndex });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
namespace Strata.SqlTools.EFCore.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a SQL query breakdown entity for Entity Framework Core mapping.
|
||||
/// This entity encapsulates the query components (SELECT, FROM, WHERE, etc.)
|
||||
/// and is designed to be compatible with EF Core DbContext and database models.
|
||||
/// </summary>
|
||||
public class QueryBreakdownEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for this query breakdown.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SELECT clause of the query.
|
||||
/// </summary>
|
||||
public string? SelectClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the SELECT clause.
|
||||
/// </summary>
|
||||
public string? SelectClauseComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the FROM clause of the query.
|
||||
/// </summary>
|
||||
public string? FromClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the FROM clause.
|
||||
/// </summary>
|
||||
public string? FromClauseComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WHERE clause of the query.
|
||||
/// </summary>
|
||||
public string? WhereClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the WHERE clause.
|
||||
/// </summary>
|
||||
public string? WhereClauseComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the GROUP BY clause of the query.
|
||||
/// </summary>
|
||||
public string? GroupByClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the GROUP BY clause.
|
||||
/// </summary>
|
||||
public string? GroupByClauseComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HAVING clause of the query.
|
||||
/// </summary>
|
||||
public string? HavingClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the HAVING clause.
|
||||
/// </summary>
|
||||
public string? HavingClauseComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ORDER BY clause of the query.
|
||||
/// </summary>
|
||||
public string? OrderByClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment for the ORDER BY clause.
|
||||
/// </summary>
|
||||
public string? OrderByClauseComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WITH clause (Common Table Expressions) as a JSON string.
|
||||
/// </summary>
|
||||
public string? WithClause { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw/original SQL statement before parsing and breakdown.
|
||||
/// </summary>
|
||||
public string? RawSql { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the setup clauses as a JSON string.
|
||||
/// These are clauses to execute before the main statement.
|
||||
/// </summary>
|
||||
public string? SetupClausesJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the finish clauses as a JSON string.
|
||||
/// These are clauses to execute after the main statement.
|
||||
/// </summary>
|
||||
public string? FinishClausesJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameters as a JSON string.
|
||||
/// Contains parameter names and their values.
|
||||
/// </summary>
|
||||
public string? ParametersJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when this entity was created.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp when this entity was last updated.
|
||||
/// </summary>
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property for the related query parameters.
|
||||
/// </summary>
|
||||
public virtual ICollection<QueryParameterEntity> Parameters { get; set; } = new List<QueryParameterEntity>();
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property for the related WITH clauses (CTEs).
|
||||
/// </summary>
|
||||
public virtual ICollection<WithClauseEntity> WithClauses { get; set; } = new List<WithClauseEntity>();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Strata.SqlTools.EFCore.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a query parameter entity for use with Entity Framework Core.
|
||||
/// Stores query parameter names and their values with type information.
|
||||
/// </summary>
|
||||
public class QueryParameterEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for this parameter.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the parent query breakdown entity.
|
||||
/// </summary>
|
||||
public int QueryBreakdownEntityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter name (e.g., "@ParameterName" or "ParameterName").
|
||||
/// </summary>
|
||||
public string ParameterName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter value as a string representation.
|
||||
/// </summary>
|
||||
public string? ParameterValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CLR type name of the parameter value for deserialization.
|
||||
/// </summary>
|
||||
public string? ParameterTypeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property to the parent QueryBreakdownEntity.
|
||||
/// </summary>
|
||||
public virtual QueryBreakdownEntity? QueryBreakdownEntity { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace Strata.SqlTools.EFCore.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a WITH clause (Common Table Expression) entity for Entity Framework Core mapping.
|
||||
/// </summary>
|
||||
public class WithClauseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier for this WITH clause.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the parent query breakdown entity.
|
||||
/// </summary>
|
||||
public int QueryBreakdownEntityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the CTE (Common Table Expression).
|
||||
/// </summary>
|
||||
public string CteName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column list for the CTE (optional).
|
||||
/// </summary>
|
||||
public string? ColumnList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the definition/query of the CTE.
|
||||
/// </summary>
|
||||
public string CteDefinition { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order of this CTE in the WITH clause.
|
||||
/// </summary>
|
||||
public int OrderIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property to the parent QueryBreakdownEntity.
|
||||
/// </summary>
|
||||
public virtual QueryBreakdownEntity? QueryBreakdownEntity { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
# Strata.SqlTools.EFCore
|
||||
|
||||
> Entity Framework Core integration and support for Strata.SqlTools QueryBreakdown functionality
|
||||
|
||||
This project provides seamless integration between the Strata.SqlTools query analysis framework and Entity Framework Core, allowing you to persist, query, and manage `QueryBreakdown` objects within your existing EF Core DbContext.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **EF Core Integration**: Map QueryBreakdown objects directly to your DbContext
|
||||
- **Entity Models**: Fully normalized entity models for QueryBreakdownEntity, QueryParameterEntity, and WithClauseEntity
|
||||
- **Automatic Mapping**: IQueryBreakdownMapper for converting between SQL Tools and EF Core models
|
||||
- **Repository Pattern**: IQueryBreakdownRepository for simplified CRUD operations
|
||||
- **DbContext Extensions**: Easy-to-use extension methods for DbContext integration
|
||||
- **JSON Serialization**: Intelligent serialization of complex types (parameters, clauses) to JSON for efficient storage
|
||||
|
||||
## Installation
|
||||
|
||||
Add the NuGet package reference:
|
||||
|
||||
```xml
|
||||
<PackageReference Include="Strata.SqlTools.EFCore" Version="1.0.0" />
|
||||
```
|
||||
|
||||
Or via the .NET CLI:
|
||||
|
||||
```bash
|
||||
dotnet add package Strata.SqlTools.EFCore
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Configure Your DbContext
|
||||
|
||||
Add the QueryBreakdown entities to your DbContext:
|
||||
|
||||
```csharp
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
using Strata.SqlTools.EFCore.Configurations;
|
||||
|
||||
public class YourDbContext : DbContext
|
||||
{
|
||||
public DbSet<QueryBreakdownEntity> QueryBreakdowns { get; set; }
|
||||
public DbSet<QueryParameterEntity> QueryParameters { get; set; }
|
||||
public DbSet<WithClauseEntity> WithClauses { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Configure QueryBreakdown entities
|
||||
modelBuilder.ConfigureQueryBreakdownEntities();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Register Services
|
||||
|
||||
Register the mapper and repository in your dependency injection container:
|
||||
|
||||
```csharp
|
||||
services.AddScoped<IQueryBreakdownMapper, QueryBreakdownMapper>();
|
||||
services.AddScoped<IQueryBreakdownRepository>(
|
||||
provider => new QueryBreakdownRepository(
|
||||
provider.GetRequiredService<YourDbContext>(),
|
||||
provider.GetRequiredService<IQueryBreakdownMapper>()
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### 3. Use the Repository
|
||||
|
||||
Inject and use the repository in your application:
|
||||
|
||||
```csharp
|
||||
public class QueryService
|
||||
{
|
||||
private readonly IQueryBreakdownRepository _repository;
|
||||
|
||||
public QueryService(IQueryBreakdownRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public async Task SaveQueryAsync(QueryBreakdown queryBreakdown)
|
||||
{
|
||||
int id = await _repository.AddAsync(queryBreakdown);
|
||||
Console.WriteLine($"Query saved with ID: {id}");
|
||||
}
|
||||
|
||||
public async Task<QueryBreakdown?> GetQueryAsync(int id)
|
||||
{
|
||||
return await _repository.GetByIdAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<QueryBreakdown>> GetAllQueriesAsync()
|
||||
{
|
||||
return await _repository.GetAllAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateQueryAsync(int id, QueryBreakdown queryBreakdown)
|
||||
{
|
||||
await _repository.UpdateAsync(id, queryBreakdown);
|
||||
}
|
||||
|
||||
public async Task DeleteQueryAsync(int id)
|
||||
{
|
||||
bool deleted = await _repository.DeleteAsync(id);
|
||||
Console.WriteLine(deleted ? "Query deleted." : "Query not found.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Entity Models
|
||||
|
||||
### QueryBreakdownEntity
|
||||
|
||||
The main entity that represents a SQL query breakdown:
|
||||
|
||||
- **Id**: Primary key
|
||||
- **SelectClause**: The SELECT clause
|
||||
- **FromClause**: The FROM clause
|
||||
- **WhereClause**: The WHERE clause
|
||||
- **GroupByClause**: The GROUP BY clause
|
||||
- **HavingClause**: The HAVING clause
|
||||
- **OrderByClause**: The ORDER BY clause
|
||||
- **WithClause**: Common Table Expressions (CTEs)
|
||||
- **RawSql**: Original SQL statement
|
||||
- **SetupClausesJson**: JSON serialized setup clauses
|
||||
- **FinishClausesJson**: JSON serialized finish clauses
|
||||
- **ParametersJson**: JSON serialized parameters
|
||||
- **CreatedAt**: Creation timestamp
|
||||
- **UpdatedAt**: Last update timestamp
|
||||
|
||||
#### Related Entities
|
||||
|
||||
- **QueryParameterEntity**: Represents parameters used in the query
|
||||
- **WithClauseEntity**: Represents individual Common Table Expressions (CTEs)
|
||||
|
||||
## Mapper Interface
|
||||
|
||||
The `IQueryBreakdownMapper` provides the following operations:
|
||||
|
||||
```csharp
|
||||
public interface IQueryBreakdownMapper
|
||||
{
|
||||
QueryBreakdownEntity MapToEntity(QueryBreakdown queryBreakdown);
|
||||
QueryBreakdown MapToDomainModel(QueryBreakdownEntity entity);
|
||||
(QueryBreakdownEntity Entity, List<QueryParameterEntity> Parameters, List<WithClauseEntity> WithClauses) MapToEntityWithRelations(QueryBreakdown queryBreakdown);
|
||||
QueryBreakdown MapToDomainModelWithRelations(QueryBreakdownEntity entity);
|
||||
}
|
||||
```
|
||||
|
||||
## Repository Interface
|
||||
|
||||
The `IQueryBreakdownRepository` provides the following operations:
|
||||
|
||||
```csharp
|
||||
public interface IQueryBreakdownRepository
|
||||
{
|
||||
Task<int> AddAsync(QueryBreakdown queryBreakdown);
|
||||
Task<QueryBreakdown?> GetByIdAsync(int id);
|
||||
Task<QueryBreakdownEntity?> GetEntityByIdAsync(int id);
|
||||
Task<List<QueryBreakdown>> GetAllAsync();
|
||||
Task<List<QueryBreakdownEntity>> GetAllEntitiesAsync();
|
||||
Task UpdateAsync(int id, QueryBreakdown queryBreakdown);
|
||||
Task<bool> DeleteAsync(int id);
|
||||
Task<int> GetCountAsync();
|
||||
}
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
The project includes three main tables:
|
||||
|
||||
### QueryBreakdowns Table
|
||||
Stores the main query breakdown information
|
||||
|
||||
### QueryParameters Table
|
||||
Stores individual query parameters with foreign key to QueryBreakdowns
|
||||
|
||||
### WithClauses Table
|
||||
Stores Common Table Expressions with foreign key to QueryBreakdowns
|
||||
|
||||
## DbContext Extension Methods
|
||||
|
||||
```csharp
|
||||
// Configure query breakdown entities during model creation
|
||||
modelBuilder.ConfigureQueryBreakdownEntities();
|
||||
|
||||
// Get queryable sets from context
|
||||
var queryBreakdowns = dbContext.GetQueryBreakdowns();
|
||||
var parameters = dbContext.GetQueryParameters();
|
||||
var withClauses = dbContext.GetWithClauses();
|
||||
|
||||
// Get a query breakdown with related data
|
||||
var entity = await dbContext.GetQueryBreakdownWithRelatedDataAsync(id);
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Custom Entity Configuration
|
||||
|
||||
If you need to customize the entity configuration, you can create your own configuration classes that implement `IEntityTypeConfiguration<T>`:
|
||||
|
||||
```csharp
|
||||
public class CustomQueryBreakdownConfiguration : IEntityTypeConfiguration<QueryBreakdownEntity>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<QueryBreakdownEntity> builder)
|
||||
{
|
||||
// Apply custom configuration
|
||||
builder.ToTable("CustomQueryBreakdowns", "dbo");
|
||||
// ... other configurations
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Working with Existing DbContext
|
||||
|
||||
If you already have an existing DbContext, simply:
|
||||
|
||||
1. Add the DbSets for QueryBreakdown entities
|
||||
2. Call `modelBuilder.ConfigureQueryBreakdownEntities()` in `OnModelCreating`
|
||||
3. Create a migration: `dotnet ef migrations add AddQueryBreakdownEntities`
|
||||
4. Update the database: `dotnet ef database update`
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Microsoft.EntityFrameworkCore** (8.0.0+)
|
||||
- **Microsoft.EntityFrameworkCore.Relational** (8.0.0+)
|
||||
- **Strata.SqlTools** (1.0.0+)
|
||||
- **Strata.SqlTools.SqlServer** (1.0.0+)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Support
|
||||
|
||||
For issues, feature requests, or questions, please visit the [GitHub repository](https://github.com/stratadecision/sql-builder).
|
||||
@@ -0,0 +1,74 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for DbContext to support QueryBreakdown entities.
|
||||
/// </summary>
|
||||
public static class DbContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds an entity configuration to the ModelBuilder for QueryBreakdown-related entities.
|
||||
/// Call this in your DbContext.OnModelCreating method.
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder">The ModelBuilder instance.</param>
|
||||
/// <returns>The ModelBuilder instance for fluent chaining.</returns>
|
||||
public static ModelBuilder ConfigureQueryBreakdownEntities(this ModelBuilder modelBuilder)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(modelBuilder);
|
||||
|
||||
modelBuilder.ApplyConfiguration(new Configurations.QueryBreakdownEntityConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new Configurations.QueryParameterEntityConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new Configurations.WithClauseEntityConfiguration());
|
||||
|
||||
return modelBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a queryable set of QueryBreakdownEntity instances from the DbContext.
|
||||
/// </summary>
|
||||
/// <param name="context">The DbContext instance.</param>
|
||||
/// <returns>An IQueryable of QueryBreakdownEntity.</returns>
|
||||
public static IQueryable<QueryBreakdownEntity> GetQueryBreakdowns(this DbContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
return context.Set<QueryBreakdownEntity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a queryable set of QueryParameterEntity instances from the DbContext.
|
||||
/// </summary>
|
||||
/// <param name="context">The DbContext instance.</param>
|
||||
/// <returns>An IQueryable of QueryParameterEntity.</returns>
|
||||
public static IQueryable<QueryParameterEntity> GetQueryParameters(this DbContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
return context.Set<QueryParameterEntity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a queryable set of WithClauseEntity instances from the DbContext.
|
||||
/// </summary>
|
||||
/// <param name="context">The DbContext instance.</param>
|
||||
/// <returns>An IQueryable of WithClauseEntity.</returns>
|
||||
public static IQueryable<WithClauseEntity> GetWithClauses(this DbContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
return context.Set<WithClauseEntity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Includes query breakdown related data and returns a single QueryBreakdownEntity by ID.
|
||||
/// </summary>
|
||||
/// <param name="context">The DbContext instance.</param>
|
||||
/// <param name="id">The ID of the QueryBreakdownEntity to retrieve.</param>
|
||||
/// <returns>The QueryBreakdownEntity with related entities included, or null if not found.</returns>
|
||||
public static async Task<QueryBreakdownEntity?> GetQueryBreakdownWithRelatedDataAsync(this DbContext context, int id)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
return await context.Set<QueryBreakdownEntity>()
|
||||
.FirstOrDefaultAsync(q => q.Id == id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using System.Collections;
|
||||
using System.Text.Json;
|
||||
using Strata.SqlTools.Breakdowns.SqlServer;
|
||||
using Strata.SqlTools.EFCore.Abstractions;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of IQueryBreakdownMapper for converting between QueryBreakdown and QueryBreakdownEntity.
|
||||
/// </summary>
|
||||
public class QueryBreakdownMapper : IQueryBreakdownMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdown (SQL Tools) to a QueryBreakdownEntity (EF Core).
|
||||
/// </summary>
|
||||
public QueryBreakdownEntity MapToEntity(QueryBreakdown queryBreakdown)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(queryBreakdown);
|
||||
|
||||
var entity = new QueryBreakdownEntity
|
||||
{
|
||||
SelectClause = queryBreakdown.SelectClause?.Clause,
|
||||
SelectClauseComment = queryBreakdown.SelectClause?.Comment,
|
||||
FromClause = queryBreakdown.FromClause?.Clause,
|
||||
FromClauseComment = queryBreakdown.FromClause?.Comment,
|
||||
WhereClause = queryBreakdown.WhereClause?.Clause,
|
||||
WhereClauseComment = queryBreakdown.WhereClause?.Comment,
|
||||
GroupByClause = queryBreakdown.GroupByClause?.Clause,
|
||||
GroupByClauseComment = queryBreakdown.GroupByClause?.Comment,
|
||||
HavingClause = queryBreakdown.HavingClause?.Clause,
|
||||
HavingClauseComment = queryBreakdown.HavingClause?.Comment,
|
||||
OrderByClause = queryBreakdown.OrderByClause?.Clause,
|
||||
OrderByClauseComment = queryBreakdown.OrderByClause?.Comment,
|
||||
WithClause = queryBreakdown.GetWithClauseValue(),
|
||||
RawSql = queryBreakdown.RawSql,
|
||||
SetupClausesJson = SerializeList(queryBreakdown.SetupClauses),
|
||||
FinishClausesJson = SerializeArrayList(queryBreakdown.FinishClauses),
|
||||
ParametersJson = SerializeDictionary(queryBreakdown.Parameters),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdownEntity (EF Core) back to a QueryBreakdown (SQL Tools).
|
||||
/// </summary>
|
||||
public QueryBreakdown MapToDomainModel(QueryBreakdownEntity entity)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
|
||||
var queryBreakdown = new QueryBreakdown();
|
||||
|
||||
// Set clause properties
|
||||
if (!string.IsNullOrEmpty(entity.SelectClause))
|
||||
{
|
||||
queryBreakdown.SelectClause.Clause = entity.SelectClause;
|
||||
queryBreakdown.SelectClause.Comment = entity.SelectClauseComment;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.FromClause))
|
||||
{
|
||||
queryBreakdown.FromClause.Clause = entity.FromClause;
|
||||
queryBreakdown.FromClause.Comment = entity.FromClauseComment;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.WhereClause))
|
||||
{
|
||||
queryBreakdown.WhereClause.Clause = entity.WhereClause;
|
||||
queryBreakdown.WhereClause.Comment = entity.WhereClauseComment;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.GroupByClause))
|
||||
{
|
||||
queryBreakdown.GroupByClause.Clause = entity.GroupByClause;
|
||||
queryBreakdown.GroupByClause.Comment = entity.GroupByClauseComment;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.HavingClause))
|
||||
{
|
||||
queryBreakdown.HavingClause.Clause = entity.HavingClause;
|
||||
queryBreakdown.HavingClause.Comment = entity.HavingClauseComment;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.OrderByClause))
|
||||
{
|
||||
queryBreakdown.OrderByClause.Clause = entity.OrderByClause;
|
||||
queryBreakdown.OrderByClause.Comment = entity.OrderByClauseComment;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.WithClause))
|
||||
{
|
||||
queryBreakdown.SetWithClauseValue(entity.WithClause);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entity.RawSql))
|
||||
{
|
||||
queryBreakdown.RawSql = entity.RawSql;
|
||||
}
|
||||
|
||||
// Restore setup clauses
|
||||
if (!string.IsNullOrEmpty(entity.SetupClausesJson))
|
||||
{
|
||||
var setupClauses = DeserializeList(entity.SetupClausesJson);
|
||||
queryBreakdown.SetupClauses.Clear();
|
||||
foreach (var clause in setupClauses)
|
||||
{
|
||||
queryBreakdown.SetupClauses.Add(clause);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore finish clauses
|
||||
if (!string.IsNullOrEmpty(entity.FinishClausesJson))
|
||||
{
|
||||
var finishClauses = DeserializeArrayList(entity.FinishClausesJson);
|
||||
queryBreakdown.FinishClauses.Clear();
|
||||
foreach (var clause in finishClauses)
|
||||
{
|
||||
queryBreakdown.FinishClauses.Add(clause);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore parameters
|
||||
if (!string.IsNullOrEmpty(entity.ParametersJson))
|
||||
{
|
||||
var parameters = DeserializeDictionary(entity.ParametersJson);
|
||||
queryBreakdown.Parameters.Clear();
|
||||
foreach (var kvp in parameters)
|
||||
{
|
||||
queryBreakdown.Parameters[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return queryBreakdown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdown to a QueryBreakdownEntity with related entities.
|
||||
/// </summary>
|
||||
public (QueryBreakdownEntity Entity, List<QueryParameterEntity> Parameters, List<WithClauseEntity> WithClauses) MapToEntityWithRelations(QueryBreakdown queryBreakdown)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(queryBreakdown);
|
||||
|
||||
var entity = MapToEntity(queryBreakdown);
|
||||
|
||||
// Map parameters
|
||||
var parameterEntities = new List<QueryParameterEntity>();
|
||||
foreach (var param in queryBreakdown.ParameterList)
|
||||
{
|
||||
parameterEntities.Add(new QueryParameterEntity
|
||||
{
|
||||
ParameterName = param.Name,
|
||||
ParameterValue = param.Value?.ToString(),
|
||||
ParameterTypeName = param.Value?.GetType().FullName
|
||||
});
|
||||
}
|
||||
|
||||
// Map WITH clauses
|
||||
var withClauseEntities = new List<WithClauseEntity>();
|
||||
int orderIndex = 0;
|
||||
foreach (var withClause in queryBreakdown.WithClauses)
|
||||
{
|
||||
withClauseEntities.Add(new WithClauseEntity
|
||||
{
|
||||
CteName = withClause.TableName,
|
||||
ColumnList = withClause.Clause,
|
||||
CteDefinition = withClause.Sql?.SelectClause?.Clause ?? string.Empty,
|
||||
OrderIndex = orderIndex++
|
||||
});
|
||||
}
|
||||
|
||||
return (entity, parameterEntities, withClauseEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a QueryBreakdownEntity with related entities back to a QueryBreakdown.
|
||||
/// </summary>
|
||||
public QueryBreakdown MapToDomainModelWithRelations(QueryBreakdownEntity entity)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
|
||||
var queryBreakdown = MapToDomainModel(entity);
|
||||
|
||||
// Reconstruct Parameters dictionary from parameter entities if they are loaded
|
||||
if (entity.Parameters != null && entity.Parameters.Count > 0)
|
||||
{
|
||||
queryBreakdown.Parameters.Clear();
|
||||
foreach (var paramEntity in entity.Parameters)
|
||||
{
|
||||
// Store with @ prefix to match how AddParameter works
|
||||
var key = paramEntity.ParameterName.StartsWith('@')
|
||||
? paramEntity.ParameterName
|
||||
: $"@{paramEntity.ParameterName}";
|
||||
|
||||
// Deserialize value if type information is available
|
||||
object? value = paramEntity.ParameterValue;
|
||||
if (!string.IsNullOrEmpty(paramEntity.ParameterTypeName) && !string.IsNullOrEmpty(paramEntity.ParameterValue))
|
||||
{
|
||||
var type = Type.GetType(paramEntity.ParameterTypeName);
|
||||
if (type != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = Convert.ChangeType(paramEntity.ParameterValue, type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If conversion fails, use string value
|
||||
value = paramEntity.ParameterValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queryBreakdown.Parameters[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return queryBreakdown;
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
internal static string SerializeList(List<string> list)
|
||||
{
|
||||
return JsonSerializer.Serialize(list);
|
||||
}
|
||||
|
||||
internal static List<string> DeserializeList(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<string>>(json) ?? new List<string>();
|
||||
}
|
||||
|
||||
internal static string SerializeArrayList(ArrayList list)
|
||||
{
|
||||
var stringList = new List<string>();
|
||||
foreach (var item in list)
|
||||
{
|
||||
stringList.Add(item?.ToString() ?? string.Empty);
|
||||
}
|
||||
return JsonSerializer.Serialize(stringList);
|
||||
}
|
||||
|
||||
internal static ArrayList DeserializeArrayList(string json)
|
||||
{
|
||||
var stringList = JsonSerializer.Deserialize<List<string>>(json) ?? new List<string>();
|
||||
var arrayList = new ArrayList();
|
||||
foreach (var item in stringList)
|
||||
{
|
||||
arrayList.Add(item);
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
internal static string SerializeDictionary(Dictionary<string, object> dict)
|
||||
{
|
||||
var stringDict = new Dictionary<string, string>();
|
||||
foreach (var kvp in dict)
|
||||
{
|
||||
stringDict[kvp.Key] = kvp.Value?.ToString() ?? string.Empty;
|
||||
}
|
||||
return JsonSerializer.Serialize(stringDict);
|
||||
}
|
||||
|
||||
internal static Dictionary<string, object> DeserializeDictionary(string json)
|
||||
{
|
||||
var stringDict = JsonSerializer.Deserialize<Dictionary<string, string>>(json) ?? new Dictionary<string, string>();
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var kvp in stringDict)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Strata.SqlTools.Breakdowns.SqlServer;
|
||||
using Strata.SqlTools.EFCore.Abstractions;
|
||||
using Strata.SqlTools.EFCore.Models;
|
||||
|
||||
namespace Strata.SqlTools.EFCore.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for a generic repository pattern for QueryBreakdown entities.
|
||||
/// Provides a simplified API for common database operations.
|
||||
/// </summary>
|
||||
public interface IQueryBreakdownRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a new QueryBreakdown to the repository and saves changes.
|
||||
/// </summary>
|
||||
/// <param name="queryBreakdown">The QueryBreakdown to add.</param>
|
||||
/// <returns>The ID of the added entity.</returns>
|
||||
Task<int> AddAsync(QueryBreakdown queryBreakdown);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a QueryBreakdown by ID and converts it from the entity.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the QueryBreakdown entity.</param>
|
||||
/// <returns>The QueryBreakdown, or null if not found.</returns>
|
||||
Task<QueryBreakdown?> GetByIdAsync(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a QueryBreakdownEntity by ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the entity.</param>
|
||||
/// <returns>The QueryBreakdownEntity, or null if not found.</returns>
|
||||
Task<QueryBreakdownEntity?> GetEntityByIdAsync(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all QueryBreakdowns.
|
||||
/// </summary>
|
||||
/// <returns>A list of all QueryBreakdowns.</returns>
|
||||
Task<List<QueryBreakdown>> GetAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all QueryBreakdownEntities.
|
||||
/// </summary>
|
||||
/// <returns>A list of all QueryBreakdownEntities.</returns>
|
||||
Task<List<QueryBreakdownEntity>> GetAllEntitiesAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing QueryBreakdown and saves changes.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the entity to update.</param>
|
||||
/// <param name="queryBreakdown">The updated QueryBreakdown.</param>
|
||||
Task UpdateAsync(int id, QueryBreakdown queryBreakdown);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a QueryBreakdown by ID and saves changes.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the entity to delete.</param>
|
||||
/// <returns>True if the entity was deleted; false if not found.</returns>
|
||||
Task<bool> DeleteAsync(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of all QueryBreakdown entities.
|
||||
/// </summary>
|
||||
/// <returns>The count of entities.</returns>
|
||||
Task<int> GetCountAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of IQueryBreakdownRepository for managing QueryBreakdown entities in Entity Framework Core.
|
||||
/// </summary>
|
||||
public class QueryBreakdownRepository : IQueryBreakdownRepository
|
||||
{
|
||||
private readonly DbContext _context;
|
||||
private readonly IQueryBreakdownMapper _mapper;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the QueryBreakdownRepository.
|
||||
/// </summary>
|
||||
/// <param name="context">The EF Core DbContext.</param>
|
||||
/// <param name="mapper">The mapper for converting between QueryBreakdown and QueryBreakdownEntity.</param>
|
||||
public QueryBreakdownRepository(DbContext context, IQueryBreakdownMapper mapper)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
ArgumentNullException.ThrowIfNull(mapper);
|
||||
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new QueryBreakdown to the repository and saves changes.
|
||||
/// </summary>
|
||||
public async Task<int> AddAsync(QueryBreakdown queryBreakdown)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(queryBreakdown);
|
||||
|
||||
var (entity, parameters, withClauses) = _mapper.MapToEntityWithRelations(queryBreakdown);
|
||||
|
||||
// Add the main entity
|
||||
_context.Set<QueryBreakdownEntity>().Add(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Add related entities with foreign key set
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
param.QueryBreakdownEntityId = entity.Id;
|
||||
_context.Set<QueryParameterEntity>().Add(param);
|
||||
}
|
||||
|
||||
foreach (var withClause in withClauses)
|
||||
{
|
||||
withClause.QueryBreakdownEntityId = entity.Id;
|
||||
_context.Set<WithClauseEntity>().Add(withClause);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
return entity.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a QueryBreakdown by ID and converts it from the entity.
|
||||
/// </summary>
|
||||
public async Task<QueryBreakdown?> GetByIdAsync(int id)
|
||||
{
|
||||
var entity = await _context.Set<QueryBreakdownEntity>()
|
||||
.Include(e => e.Parameters)
|
||||
.Include(e => e.WithClauses)
|
||||
.FirstOrDefaultAsync(e => e.Id == id);
|
||||
|
||||
return entity != null ? _mapper.MapToDomainModelWithRelations(entity) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a QueryBreakdownEntity by ID.
|
||||
/// </summary>
|
||||
public async Task<QueryBreakdownEntity?> GetEntityByIdAsync(int id)
|
||||
{
|
||||
return await _context.Set<QueryBreakdownEntity>()
|
||||
.FirstOrDefaultAsync(e => e.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all QueryBreakdowns.
|
||||
/// </summary>
|
||||
public async Task<List<QueryBreakdown>> GetAllAsync()
|
||||
{
|
||||
var entities = await _context.Set<QueryBreakdownEntity>().ToListAsync();
|
||||
return entities.ConvertAll(e => _mapper.MapToDomainModel(e));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all QueryBreakdownEntities.
|
||||
/// </summary>
|
||||
public async Task<List<QueryBreakdownEntity>> GetAllEntitiesAsync()
|
||||
{
|
||||
return await _context.Set<QueryBreakdownEntity>().ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing QueryBreakdown and saves changes.
|
||||
/// </summary>
|
||||
public async Task UpdateAsync(int id, QueryBreakdown queryBreakdown)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(queryBreakdown);
|
||||
|
||||
var entity = await _context.Set<QueryBreakdownEntity>().FirstOrDefaultAsync(e => e.Id == id);
|
||||
if (entity == null)
|
||||
{
|
||||
throw new InvalidOperationException($"QueryBreakdown with ID {id} not found.");
|
||||
}
|
||||
|
||||
var updatedEntity = _mapper.MapToEntity(queryBreakdown);
|
||||
|
||||
// Update the main entity
|
||||
entity.SelectClause = updatedEntity.SelectClause;
|
||||
entity.SelectClauseComment = updatedEntity.SelectClauseComment;
|
||||
entity.FromClause = updatedEntity.FromClause;
|
||||
entity.FromClauseComment = updatedEntity.FromClauseComment;
|
||||
entity.WhereClause = updatedEntity.WhereClause;
|
||||
entity.WhereClauseComment = updatedEntity.WhereClauseComment;
|
||||
entity.GroupByClause = updatedEntity.GroupByClause;
|
||||
entity.GroupByClauseComment = updatedEntity.GroupByClauseComment;
|
||||
entity.HavingClause = updatedEntity.HavingClause;
|
||||
entity.HavingClauseComment = updatedEntity.HavingClauseComment;
|
||||
entity.OrderByClause = updatedEntity.OrderByClause;
|
||||
entity.OrderByClauseComment = updatedEntity.OrderByClauseComment;
|
||||
entity.WithClause = updatedEntity.WithClause;
|
||||
entity.RawSql = updatedEntity.RawSql;
|
||||
entity.SetupClausesJson = updatedEntity.SetupClausesJson;
|
||||
entity.FinishClausesJson = updatedEntity.FinishClausesJson;
|
||||
entity.ParametersJson = updatedEntity.ParametersJson;
|
||||
entity.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Delete and recreate related entities
|
||||
var existingParameters = _context.Set<QueryParameterEntity>().Where(p => p.QueryBreakdownEntityId == id);
|
||||
_context.Set<QueryParameterEntity>().RemoveRange(existingParameters);
|
||||
|
||||
var existingWithClauses = _context.Set<WithClauseEntity>().Where(w => w.QueryBreakdownEntityId == id);
|
||||
_context.Set<WithClauseEntity>().RemoveRange(existingWithClauses);
|
||||
|
||||
var (_, parameters, withClauses) = _mapper.MapToEntityWithRelations(queryBreakdown);
|
||||
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
param.QueryBreakdownEntityId = id;
|
||||
_context.Set<QueryParameterEntity>().Add(param);
|
||||
}
|
||||
|
||||
foreach (var withClause in withClauses)
|
||||
{
|
||||
withClause.QueryBreakdownEntityId = id;
|
||||
_context.Set<WithClauseEntity>().Add(withClause);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a QueryBreakdown by ID and saves changes.
|
||||
/// </summary>
|
||||
public async Task<bool> DeleteAsync(int id)
|
||||
{
|
||||
var entity = await _context.Set<QueryBreakdownEntity>().FirstOrDefaultAsync(e => e.Id == id);
|
||||
if (entity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_context.Set<QueryBreakdownEntity>().Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of all QueryBreakdown entities.
|
||||
/// </summary>
|
||||
public async Task<int> GetCountAsync()
|
||||
{
|
||||
return await _context.Set<QueryBreakdownEntity>().CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<!-- NuGet Package Metadata -->
|
||||
<PackageId>Strata.SqlTools.EFCore</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>Strata Decision Technology</Authors>
|
||||
<Company>Strata Decision Technology</Company>
|
||||
<Product>Strata SQL Utilities - EF Core</Product>
|
||||
<Description>Entity Framework Core integration and support for Strata.SqlTools QueryBreakdown functionality, allowing seamless mapping of SQL query breakdowns onto existing DbContext and database models.</Description>
|
||||
<PackageTags>sql;efcore;entity-framework;query-builder;database;orm</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/stratadecision/sql-builder</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/stratadecision/sql-builder</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageReleaseNotes>Initial release with EF Core integration for QueryBreakdown functionality.</PackageReleaseNotes>
|
||||
<Copyright>Copyright © Strata Decision Technology 2024-2026</Copyright>
|
||||
|
||||
<!-- Build Configuration -->
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>symbols.nupkg</SymbolPackageFormat>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
|
||||
<!-- Code Analysis -->
|
||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
<AnalysisLevel>latest</AnalysisLevel>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Strata.SqlTools.SqlBreakdown\Strata.SqlTools.SqlBreakdown.csproj" />
|
||||
<ProjectReference Include="..\Strata.SqlTools.SqlServer\Strata.SqlTools.SqlServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user