diff --git a/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/IVogen.cs b/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/IVogen.cs index ad1c7da8..baa97116 100644 --- a/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/IVogen.cs +++ b/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/IVogen.cs @@ -24,7 +24,7 @@ public interface IVogen static abstract bool Equals(TVogen left, TVogen right, IEqualityComparer comparer); static abstract int CompareTo(TVogen left, TVogen right); - + TValueObject Value { get; } bool IsInitialized(); diff --git a/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/VogenSchemaFilter.cs b/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/VogenSchemaFilter.cs index 1e0e0354..da7e3b14 100644 --- a/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/VogenSchemaFilter.cs +++ b/src/BUTR.Site.NexusMods.Server.ValueObjects.Vogen/Utils/VogenSchemaFilter.cs @@ -1,4 +1,5 @@ using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; namespace BUTR.Site.NexusMods.Server.ValueObjects.Utils; @@ -26,7 +27,7 @@ private static T CopyPublicProperties(T oldObject, T newObject) where T : cla return newObject; } - + public void Apply(OpenApiSchema schema, SchemaFilterContext context) { if (context.Type.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IVogen<,>)) is not { } vogen) diff --git a/src/BUTR.Site.NexusMods.Server/Jobs/TopExceptionsTypesAnalyzerProcessorJob.cs b/src/BUTR.Site.NexusMods.Server/Jobs/TopExceptionsTypesAnalyzerProcessorJob.cs index ac234280..29f54f9c 100644 --- a/src/BUTR.Site.NexusMods.Server/Jobs/TopExceptionsTypesAnalyzerProcessorJob.cs +++ b/src/BUTR.Site.NexusMods.Server/Jobs/TopExceptionsTypesAnalyzerProcessorJob.cs @@ -46,20 +46,9 @@ public async Task Execute(IJobExecutionContext context) private async Task HandleTenantAsync(AsyncServiceScope scope, TenantId tenant, CancellationToken ct) { var unitOfWorkFactory = scope.ServiceProvider.GetRequiredService(); - await using var unitOfRead = unitOfWorkFactory.CreateUnitOfRead(); await using var unitOfWrite = unitOfWorkFactory.CreateUnitOfWrite(); - var exceptionTypes = await unitOfRead.ExceptionTypes.GetAllAsync(null, null, ct); - var statistics = exceptionTypes.Select(x => new StatisticsTopExceptionsTypeEntity - { - TenantId = tenant, - ExceptionTypeId = x.ExceptionTypeId, - ExceptionType = unitOfWrite.UpsertEntityFactory.GetOrCreateExceptionType(x.ExceptionTypeId), - ExceptionCount = x.ToCrashReports.Count - }).ToList(); - - unitOfWrite.StatisticsTopExceptionsTypes.Remove(x => true); - unitOfWrite.StatisticsTopExceptionsTypes.UpsertRange(statistics); + await unitOfWrite.StatisticsTopExceptionsTypes.CalculateAsync(ct); await unitOfWrite.SaveChangesAsync(CancellationToken.None); } diff --git a/src/BUTR.Site.NexusMods.Server/Program.cs b/src/BUTR.Site.NexusMods.Server/Program.cs index 583ae2e7..da932569 100644 --- a/src/BUTR.Site.NexusMods.Server/Program.cs +++ b/src/BUTR.Site.NexusMods.Server/Program.cs @@ -147,7 +147,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host var loggingEndpoint = oltpSection.GetValue("LoggingEndpoint"); if (loggingEndpoint is null) return; var loggingProtocol = oltpSection.GetValue("LoggingProtocol"); - + builder.AddOpenTelemetry(o => { o.IncludeScopes = true; diff --git a/src/BUTR.Site.NexusMods.Server/Repositories/StatisticsTopExceptionsTypeEntityRepository.cs b/src/BUTR.Site.NexusMods.Server/Repositories/StatisticsTopExceptionsTypeEntityRepository.cs index 407b063e..434eefd3 100644 --- a/src/BUTR.Site.NexusMods.Server/Repositories/StatisticsTopExceptionsTypeEntityRepository.cs +++ b/src/BUTR.Site.NexusMods.Server/Repositories/StatisticsTopExceptionsTypeEntityRepository.cs @@ -2,20 +2,51 @@ using BUTR.Site.NexusMods.Server.Contexts; using BUTR.Site.NexusMods.Server.Models.Database; +using EFCore.BulkExtensions; + using Microsoft.EntityFrameworkCore; using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace BUTR.Site.NexusMods.Server.Repositories; public interface IStatisticsTopExceptionsTypeEntityRepositoryRead : IRepositoryRead; -public interface IStatisticsTopExceptionsTypeEntityRepositoryWrite : IRepositoryWrite, IStatisticsTopExceptionsTypeEntityRepositoryRead; +public interface IStatisticsTopExceptionsTypeEntityRepositoryWrite : IRepositoryWrite, IStatisticsTopExceptionsTypeEntityRepositoryRead +{ + Task CalculateAsync(CancellationToken ct); +} [ScopedService] internal class StatisticsTopExceptionsTypeEntityRepository : Repository, IStatisticsTopExceptionsTypeEntityRepositoryWrite { + private readonly ITenantContextAccessor _tenantContextAccessor; + + public StatisticsTopExceptionsTypeEntityRepository(IAppDbContextProvider appDbContextProvider, ITenantContextAccessor tenantContextAccessor) : base(appDbContextProvider.Get()) + { + _tenantContextAccessor = tenantContextAccessor; + } + protected override IQueryable InternalQuery => base.InternalQuery .Include(x => x.ExceptionType); - public StatisticsTopExceptionsTypeEntityRepository(IAppDbContextProvider appDbContextProvider) : base(appDbContextProvider.Get()) { } + public async Task CalculateAsync(CancellationToken ct) + { + if (_dbContext is not AppDbContextWrite dbContextWrite) + throw AppDbContextRead.WriteNotSupported(); + + var tenant = _tenantContextAccessor.Current; + var upsertEntityFactory = dbContextWrite.GetEntityFactory(); + var statistics = dbContextWrite.ExceptionTypes.Select(x => new StatisticsTopExceptionsTypeEntity + { + TenantId = tenant, + ExceptionTypeId = x.ExceptionTypeId, + ExceptionType = upsertEntityFactory.GetOrCreateExceptionType(x.ExceptionTypeId), + ExceptionCount = x.ToCrashReports.Count + }).ToList(); + + await dbContextWrite.StatisticsTopExceptionsTypes.Where(x => true).ExecuteDeleteAsync(cancellationToken: ct); + await dbContextWrite.BulkInsertOrUpdateAsync(statistics, cancellationToken: ct); + } } \ No newline at end of file