| File: DatabaseDeveloperPageExceptionFilter.cs | Web Access |
| Project: src\aspnetcore\src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj (Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Data.Common; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore; /// <summary> /// An <see cref="IDeveloperPageExceptionFilter"/> that captures database-related exceptions that can be resolved by using Entity Framework migrations. /// When these exceptions occur, an HTML response with details about possible actions to resolve the issue is generated. /// </summary> /// <remarks> /// This should only be enabled in the Development environment. /// </remarks> [RequiresDynamicCode("DbContext migrations operations are not supported with NativeAOT")] public sealed class DatabaseDeveloperPageExceptionFilter : IDeveloperPageExceptionFilter { private readonly ILogger _logger; private readonly DatabaseErrorPageOptions _options; /// <summary> /// Initializes a new instance of <see cref="DatabaseDeveloperPageExceptionFilter"/>. /// </summary> /// <param name="logger">The <see cref="ILogger"/>.</param> /// <param name="options">The <see cref="IOptions{DatabaseErrorPageOptions}"/>.</param> public DatabaseDeveloperPageExceptionFilter(ILogger<DatabaseDeveloperPageExceptionFilter> logger, IOptions<DatabaseErrorPageOptions> options) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); } /// <summary> /// Handle <see cref="DbException"/> errors and output an HTML response with additional details. /// </summary> /// <inheritdoc /> public async Task HandleExceptionAsync(ErrorContext errorContext, Func<ErrorContext, Task> next) { var dbException = errorContext.Exception as DbException ?? errorContext.Exception?.InnerException as DbException; if (dbException == null) { await next(errorContext); return; } try { // Look for DbContext classes registered in the service provider var registeredContexts = errorContext.HttpContext.RequestServices.GetServices<DbContextOptions>() .Select(o => o.ContextType) .Distinct(); // Workaround for https://github.com/dotnet/efcore/issues/22341 if (registeredContexts.Any()) { var contextDetails = new List<DatabaseContextDetails>(); foreach (var registeredContext in registeredContexts) { var details = await errorContext.HttpContext.GetContextDetailsAsync(registeredContext, _logger); if (details != null) { contextDetails.Add(details); } } if (contextDetails.Any(c => c.PendingModelChanges || c.PendingMigrations.Any())) { var page = new DatabaseErrorPage { Model = new DatabaseErrorPageModel(dbException, contextDetails, _options, errorContext.HttpContext.Request.PathBase) }; await page.ExecuteAsync(errorContext.HttpContext); return; } } } catch (Exception e) { _logger.DatabaseErrorPageMiddlewareException(e); } // Error could not be handled var response = errorContext.HttpContext.Response; if (response.HasStarted) { _logger.ResponseStartedDatabaseDeveloperPageExceptionFilter(); return; } // Try the next filter response.Clear(); response.StatusCode = 500; await next(errorContext); } }