Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Audit/AuditModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avolutions.Baf.Core.Audit.Services;
using Avolutions.Baf.Core.Module.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Avolutions.Baf.Core.Audit;

Expand All @@ -11,6 +12,6 @@ public class AuditModule : IFeatureModule
public void Register(IServiceCollection services)
{
services.AddScoped(typeof(IAuditLogService<>), typeof(AuditLogService<>));
services.AddScoped<AuditSaveChangesInterceptor>();
services.TryAddSingleton<AuditSaveChangesInterceptor>();
}
}
2 changes: 1 addition & 1 deletion src/Avolutions.Baf.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Nullable>enable</Nullable>

<PackageId>Avolutions.Baf.Core</PackageId>
<Version>0.19.0</Version>
<Version>0.20.0</Version>

<Title>Avolutions BAF Core</Title>
<Company>Avolutions</Company>
Expand Down
3 changes: 2 additions & 1 deletion src/Entity/EntityModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avolutions.Baf.Core.Entity.Services;
using Avolutions.Baf.Core.Module.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Avolutions.Baf.Core.Entity;

Expand All @@ -11,7 +12,7 @@ public class EntityModule : IFeatureModule
public void Register(IServiceCollection services)
{
services.AddScoped(typeof(IEntityService<>), typeof(EntityService<>));
services.AddScoped<TrackableSaveChangesInterceptor>();
services.TryAddSingleton<TrackableSaveChangesInterceptor>();
services.AddSingleton(typeof(IEntityRouteProvider<>), typeof(EntityRouteProvider<>));
}
}
1 change: 1 addition & 0 deletions src/Identity/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Avolutions.Baf.Core.Identity.Models;
using Avolutions.Baf.Core.Identity.Services;
using Avolutions.Baf.Core.Persistence;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
Expand Down
1 change: 1 addition & 0 deletions src/Jobs/Infrastructure/JobService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avolutions.Baf.Core.Identity.Models;
using Avolutions.Baf.Core.Jobs.Abstractions;
using Avolutions.Baf.Core.Jobs.Models;
using Avolutions.Baf.Core.Persistence;
using Microsoft.EntityFrameworkCore;

namespace Avolutions.Baf.Core.Jobs.Infrastructure;
Expand Down
1 change: 1 addition & 0 deletions src/Jobs/Infrastructure/JobWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Channels;
using Avolutions.Baf.Core.Jobs.Abstractions;
using Avolutions.Baf.Core.Jobs.Models;
using Avolutions.Baf.Core.Persistence;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Expand Down
12 changes: 11 additions & 1 deletion src/Module/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avolutions.Baf.Core.Entity.Interceptors;
using Avolutions.Baf.Core.Lookups.Interceptors;
using Avolutions.Baf.Core.Module.Abstractions;
using Avolutions.Baf.Core.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
Expand Down Expand Up @@ -33,6 +34,15 @@
public static IServiceCollection AddBafCore<TContext>(this IServiceCollection services, params Assembly[] assemblies)
where TContext : BafDbContext
{
// Register a factory alias so framework services (e.g., EntityService<T>) can depend on
// IDbContextFactory<BafDbContext> without knowing the concrete context type.
// This is the factory equivalent of the scoped DbContext alias below.
services.TryAddScoped<IDbContextFactory<BafDbContext>>(sp =>
new BafDbContextFactoryWrapper<TContext>(sp.GetRequiredService<IDbContextFactory<TContext>>()));

// Keep scoped aliases for backward compatibility.
// AddDbContextFactory already registers TContext as scoped, so API controllers
// and other non-Blazor code that injects DbContext/BafDbContext directly still works.
services.TryAddScoped<DbContext>(sp => sp.GetRequiredService<TContext>());
services.TryAddScoped<BafDbContext>(sp => sp.GetRequiredService<TContext>());

Expand All @@ -48,7 +58,7 @@
services.AddSingleton(new BafRegistry(modules, moduleAssemblies));

// Add database context interceptors
services.AddDbContext<TContext>((sp, options) =>
services.AddDbContextFactory<TContext>((sp, options) =>
{
options.AddInterceptors(
sp.GetRequiredService<AuditSaveChangesInterceptor>(),
Expand Down Expand Up @@ -99,7 +109,7 @@
.ToArray();

var distinctAssemblies = moduleTypes
.Select(t => t.Assembly)

Check warning on line 112 in src/Module/Extensions/ServiceCollectionExtensions.cs

View workflow job for this annotation

GitHub Actions / publish

Dereference of a possibly null reference.

Check warning on line 112 in src/Module/Extensions/ServiceCollectionExtensions.cs

View workflow job for this annotation

GitHub Actions / publish

Dereference of a possibly null reference.
.Distinct()
.ToArray();

Expand Down
1 change: 1 addition & 0 deletions src/NumberSequences/Services/NumberSequenceService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Avolutions.Baf.Core.NumberSequences.Models;
using Avolutions.Baf.Core.Persistence;
using Microsoft.EntityFrameworkCore;

namespace Avolutions.Baf.Core.NumberSequences.Services;
Expand Down
4 changes: 1 addition & 3 deletions src/BafDbContext.cs → src/Persistence/BafDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
using Avolutions.Baf.Core.Jobs.Models;
using Avolutions.Baf.Core.NumberSequences.Models;
using Avolutions.Baf.Core.Persistence.Extensions;
using Avolutions.Baf.Core.Reports.Models;
using Avolutions.Baf.Core.Settings.Models;
using Avolutions.Baf.Core.Setup.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace Avolutions.Baf.Core;
namespace Avolutions.Baf.Core.Persistence;

/// <summary>
/// Base DbContext for the BAF framework.
Expand All @@ -29,7 +28,6 @@ public class BafDbContext : IdentityDbContext<User, Role, Guid>
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
public DbSet<JobRun> JobRuns => Set<JobRun>();
public DbSet<NumberSequence> NumberSequences => Set<NumberSequence>();
public DbSet<Report> Reports => Set<Report>();
public DbSet<Setting> Settings => Set<Setting>();
public DbSet<SetupStatus> SetupStatus => Set<SetupStatus>();

Expand Down
19 changes: 19 additions & 0 deletions src/Persistence/BafDbContextFactoryWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;

namespace Avolutions.Baf.Core.Persistence;

internal class BafDbContextFactoryWrapper<TContext> : IDbContextFactory<BafDbContext>
where TContext : BafDbContext
{
private readonly IDbContextFactory<TContext> _inner;

public BafDbContextFactoryWrapper(IDbContextFactory<TContext> inner)
{
_inner = inner;
}

public BafDbContext CreateDbContext()
{
return _inner.CreateDbContext();
}
}
9 changes: 9 additions & 0 deletions src/Reports/Abstractions/IReport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Avolutions.Baf.Core.Reports.Abstractions;

public interface IReport
{
string ContentTemplatePath { get; }
string? HeaderTemplatePath { get; }
string? FooterTemplatePath { get; }
Task<IReportModel> BuildModelAsync(IReportArgs args, CancellationToken ct = default);
}
7 changes: 7 additions & 0 deletions src/Reports/Abstractions/IReportService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Avolutions.Baf.Core.Reports.Abstractions;

public interface IReportService
{
Task<byte[]> RenderPdfAsync<TReport>(IReportArgs args, CancellationToken ct = default)
where TReport : class, IReport;
}
6 changes: 0 additions & 6 deletions src/Reports/Abstractions/IReportWithKey.cs

This file was deleted.

42 changes: 0 additions & 42 deletions src/Reports/Configurations/ReportConfiguration.cs

This file was deleted.

72 changes: 23 additions & 49 deletions src/Reports/Models/Report.cs
Original file line number Diff line number Diff line change
@@ -1,61 +1,35 @@
using System.ComponentModel.DataAnnotations.Schema;
using Avolutions.Baf.Core.Entity.Models;
using Avolutions.Baf.Core.Reports.Abstractions;
using Microsoft.EntityFrameworkCore;
using Avolutions.Baf.Core.Reports.Abstractions;

namespace Avolutions.Baf.Core.Reports.Models;

public abstract class Report : EntityBase
public abstract class Report<TModel, TArgs> : IReport
where TModel : IReportModel
where TArgs : IReportArgs
{
public string Key { get; set; } = string.Empty;
public string Json { get; set; } = string.Empty;
public string HeaderHtml { get; set; } = string.Empty;
public string ContentHtml { get; set; } = string.Empty;
public string FooterHtml { get; set; } = string.Empty;
private string? _basePath;

// For tooling / reflection
public abstract Type ModelType { get; }
public abstract Type ArgsType { get; }
private string BasePath => _basePath ??= GetBasePath();

public abstract Task<IReportModel> BuildModelAsync(
DbContext db, IReportArgs? args, CancellationToken ct);

public abstract Task<IReportModel> BuildDemoAsync(
DbContext db, CancellationToken ct);
}

public abstract class Report<TModel, TArgs> : Report
where TModel : class, IReportModel, new()
where TArgs : class, IReportArgs, new()
{
[NotMapped]
public TModel? Model { get; private set; }

public sealed override Type ModelType => typeof(TModel);
public sealed override Type ArgsType => typeof(TArgs);

protected abstract Task<TModel> BuildTypedModelAsync(
DbContext db, TArgs args, CancellationToken ct);

protected abstract Task<TModel> BuildTypedDemoAsync(
DbContext db, CancellationToken ct);

public sealed override async Task<IReportModel> BuildModelAsync(
DbContext db, IReportArgs? args, CancellationToken ct)
private string GetBasePath()
{
if (args is not TArgs typed)
var ns = GetType().Namespace?.Replace(".", "/") ?? "";
var index = ns.IndexOf('/');
if (index > 0)
{
throw new ArgumentException(
$"Report args must be of type {typeof(TArgs).Name}. " +
$"Received {args?.GetType().Name ?? "null"}.", nameof(args));
ns = ns[(index + 1)..];
}

return await BuildTypedModelAsync(db, typed, ct).ContinueWith<IReportModel>(t => t.Result, ct);
return Path.Combine(AppContext.BaseDirectory, ns, "Templates");
}

public sealed override async Task<IReportModel> BuildDemoAsync(
DbContext db, CancellationToken ct)
{
return await BuildTypedDemoAsync(db, ct);
}
protected string TemplatePath(string fileName) => Path.Combine(BasePath, fileName);

public abstract string ContentTemplatePath { get; }
public virtual string? HeaderTemplatePath => null;
public virtual string? FooterTemplatePath => null;

public abstract Task<TModel> BuildModelAsync(TArgs args, CancellationToken ct = default);

async Task<IReportModel> IReport.BuildModelAsync(IReportArgs args, CancellationToken ct)
=> await BuildModelAsync((TArgs)args, ct);
}
8 changes: 3 additions & 5 deletions src/Reports/ReportsModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Avolutions.Baf.Core.Entity.Abstractions;
using Avolutions.Baf.Core.Module.Abstractions;
using Avolutions.Baf.Core.Reports.Models;
using Avolutions.Baf.Core.Module.Abstractions;
using Avolutions.Baf.Core.Reports.Abstractions;
using Avolutions.Baf.Core.Reports.Services;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -10,7 +9,6 @@ public class ReportsModule : IFeatureModule
{
public void Register(IServiceCollection services)
{
services.AddScoped<ReportService>();
services.AddScoped<IEntityService<Report>, ReportService>();
services.AddScoped<IReportService, ReportService>();
}
}
Loading
Loading