diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs index efc4ba99c..81b91aa65 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs @@ -47,6 +47,11 @@ public class LearningHubConfig /// public bool UseRedisCache { get; set; } = false; + /// + /// Gets or sets . + /// + public int MaxDatabaseRetryAttempts { get; set; } = 0; + /// /// Gets or sets . /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs index a3a35abcc..2b2dad930 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs @@ -187,11 +187,21 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon // External services.AddSingleton(); - var dbContextOptions = new DbContextOptionsBuilder() - .UseSqlServer(configuration.GetConnectionString("LearningHubDbConnection")).Options; + // Configure LearningHubDbContextOptions per scope (per request) + services.AddScoped(sp => + { + var dbOptions = sp.GetRequiredService>(); + var mappings = sp.GetServices(); + return new LearningHubDbContextOptions(dbOptions, mappings); + }); - services.AddSingleton(dbContextOptions); - services.AddSingleton(); + // Configure DbContext + var maxDatabaseRetryAttempts = configuration.GetValue("LearningHub:MaxDatabaseRetryAttempts"); + services.AddDbContext(options => + options.UseSqlServer( + configuration.GetConnectionString("LearningHubDbConnection"), + sqlOptions => sqlOptions.EnableRetryOnFailure(maxDatabaseRetryAttempts) + )); } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Activity/ScormActivityRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Activity/ScormActivityRepository.cs index cfcef4ebb..800062297 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Activity/ScormActivityRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Activity/ScormActivityRepository.cs @@ -220,12 +220,13 @@ public async Task CheckUserScormActivitySuspendDataToBeCleared(int lastSco private async Task UpdateScormActivityAsync(int userId, ScormActivity updatedScormActivity) { - var existingScormActivity = DbContext.ScormActivity.Where(s => s.Id == updatedScormActivity.Id) + var existingScormActivity = this.DbContext.ScormActivity.Where(s => s.Id == updatedScormActivity.Id) .Include(sao => sao.ScormActivityObjective) .Include(sao => sao.ScormActivityInteraction) .ThenInclude(sai => sai.ScormActivityInteractionCorrectResponse) .Include(sai => sai.ScormActivityInteraction) .ThenInclude(sai => sai.ScormActivityInteractionObjective) + .AsSplitQuery() .SingleOrDefault(); updatedScormActivity.ResourceActivityId = existingScormActivity.ResourceActivityId; diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserProfileRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserProfileRepository.cs index b3edc6e34..1d18d4eee 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserProfileRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserProfileRepository.cs @@ -26,9 +26,9 @@ public UserProfileRepository(LearningHubDbContext context, ITimezoneOffsetManage /// /// The id. /// The userProfile. - public Task GetByIdAsync(int id) + public async Task GetByIdAsync(int id) { - return DbContext.UserProfile.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id); + return await DbContext.UserProfile.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id); } /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/CatalogueService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/CatalogueService.cs index 9c78cdb0d..b5f934976 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/CatalogueService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/CatalogueService.cs @@ -543,7 +543,7 @@ public async Task GetCatalogueAsync(string reference, int us await this.RecordNodeActivity(userId, catalogue); var catalogueVM = this.mapper.Map(catalogue); - var bookmark = this.bookmarkRepository.GetAll().Where(b => b.NodeId == catalogue.NodeId && b.UserId == userId).FirstOrDefault(); + var bookmark = await this.bookmarkRepository.GetAll().Where(b => b.NodeId == catalogue.NodeId && b.UserId == userId).FirstOrDefaultAsync(); catalogueVM.BookmarkId = bookmark?.Id; catalogueVM.IsBookmarked = !bookmark?.Deleted ?? false; catalogueVM.Providers = await this.providerService.GetByCatalogueVersionIdAsync(catalogueVM.Id); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs index 4adaebe2b..421047223 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/BookmarkController.cs @@ -37,15 +37,17 @@ public BookmarkController(IBookmarkService bookmarkService) [Route("GetAllByParent/{parentId?}")] public async Task GetAllByParent(int? parentId, bool? all = false) { - if (this.CurrentUserId.GetValueOrDefault() != null) + var userId = this.CurrentUserId; + + if (userId.HasValue && userId.Value != 4) { - var bookmarks = await this.bookmarkService.GetAllByParent(this.CurrentUserId.GetValueOrDefault(), parentId, all); + var bookmarks = await this.bookmarkService.GetAllByParent(userId.Value, parentId, all); return this.Ok(bookmarks); } - else - { - return this.Ok(await this.bookmarkService.GetAllByParent(this.TokenWithoutBearer)); - } + + var fallbackBookmarks = await this.bookmarkService.GetAllByParent(this.TokenWithoutBearer); + return this.Ok(fallbackBookmarks); + } /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs index d28333990..85ea14ce6 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs @@ -11,6 +11,8 @@ /// public abstract class OpenApiControllerBase : ControllerBase, IDisposable { + private const int PortalAdminId = 4; + /// /// Gets the current user's ID. /// @@ -28,14 +30,14 @@ public int? CurrentUserId } else { - // If parsing fails, return null - for apikey this will be the name - return null; + // If parsing fails, return PortalAdminId as default userId - for apikey this will be the name + return PortalAdminId; } } else { // When authorizing by ApiKey we do not have a user for example - return null; + return PortalAdminId; } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs index dfe34b2b8..68a10c876 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs @@ -84,10 +84,6 @@ public void ConfigureServices(IServiceCollection services) services.AddRepositories(this.Configuration); services.AddServices(); - - services.AddDbContext( - options => - options.UseSqlServer(this.Configuration.GetConnectionString("LearningHub"))); services.AddApplicationInsightsTelemetry(); services.AddControllers(options => { diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json b/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json index 7142f4b36..1be62cf11 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json +++ b/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.json @@ -76,6 +76,7 @@ "SupportPages": "https://support.learninghub.nhs.uk/", "SupportForm": "https://support.learninghub.nhs.uk/support/tickets/new", "UseRedisCache": true, + "MaxDatabaseRetryAttempts": 3, "ResourcePublishQueueRouteName": "", "HierarchyEditPublishQueueName": "", "ContentManagementQueueName": "", diff --git a/WebAPI/LearningHub.Nhs.Repository/Activity/ScormActivityRepository.cs b/WebAPI/LearningHub.Nhs.Repository/Activity/ScormActivityRepository.cs index 6d9af606a..5f2fd0863 100644 --- a/WebAPI/LearningHub.Nhs.Repository/Activity/ScormActivityRepository.cs +++ b/WebAPI/LearningHub.Nhs.Repository/Activity/ScormActivityRepository.cs @@ -226,6 +226,7 @@ private async Task UpdateScormActivityAsync(int userId, ScormActivity updatedSco .ThenInclude(sai => sai.ScormActivityInteractionCorrectResponse) .Include(sai => sai.ScormActivityInteraction) .ThenInclude(sai => sai.ScormActivityInteractionObjective) + .AsSplitQuery() .SingleOrDefault(); updatedScormActivity.ResourceActivityId = existingScormActivity.ResourceActivityId;