/// <summary> /// Process an individual request. /// </summary> /// <param name="context">The context for the current request.</param> /// <returns>A task that represents the asynchronous operation.</returns> public virtual async Task Invoke([NotNull] HttpContext context) { Check.NotNull(context, "context"); try { #if !NETSTANDARD1_3 // TODO This probably isn't the correct place for this workaround, it // needs to be called before anything is written to CallContext // http://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx System.Configuration.ConfigurationManager.GetSection("system.xml/xmlReader"); #endif _loggerProvider.Logger.StartLoggingForCurrentCallContext(); await _next(context); } catch (Exception ex) { try { if (ShouldDisplayErrorPage(_loggerProvider.Logger.LastError, ex, _logger)) { var dbContextType = _loggerProvider.Logger.LastError.ContextType; var dbContext = (DbContext)context.RequestServices.GetService(dbContextType); if (dbContext == null) { _logger.LogError(Strings.FormatDatabaseErrorPageMiddleware_ContextNotRegistered(dbContextType.FullName)); } else { var creator = dbContext.GetService <IDatabaseCreator>() as IRelationalDatabaseCreator; if (creator == null) { _logger.LogDebug(Strings.DatabaseErrorPage_NotRelationalDatabase); } else { var databaseExists = creator.Exists(); var historyRepository = dbContext.GetService <IHistoryRepository>(); var migrationsAssembly = dbContext.GetService <IMigrationsAssembly>(); var modelDiffer = dbContext.GetService <IMigrationsModelDiffer>(); var appliedMigrations = historyRepository.GetAppliedMigrations(); var pendingMigrations = ( from m in migrationsAssembly.Migrations where !appliedMigrations.Any( r => string.Equals(r.MigrationId, m.Key, StringComparison.OrdinalIgnoreCase)) select m.Key) .ToList(); // HasDifferences will return true if there is no model snapshot, but if there is an existing database // and no model snapshot then we don't want to show the error page since they are most likely targeting // and existing database and have just misconfigured their model var pendingModelChanges = migrationsAssembly.ModelSnapshot == null && databaseExists ? false : modelDiffer.HasDifferences(migrationsAssembly.ModelSnapshot?.Model, dbContext.Model); if ((!databaseExists && pendingMigrations.Any()) || pendingMigrations.Any() || pendingModelChanges) { var page = new DatabaseErrorPage(); page.Model = new DatabaseErrorPageModel(dbContextType, ex, databaseExists, pendingModelChanges, pendingMigrations, _options); await page.ExecuteAsync(context); return; } } } } } catch (Exception e) { _logger.LogError(0, e, Strings.DatabaseErrorPageMiddleware_Exception); } throw; } }
/// <summary> /// Process an individual request. /// </summary> /// <param name="httpContext">The HTTP context for the current request.</param> /// <returns>A task that represents the asynchronous operation.</returns> public virtual async Task Invoke(HttpContext httpContext) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } try { // Because CallContext is cloned at each async operation we cannot // lazily create the error object when an error is encountered, otherwise // it will not be available to code outside of the current async context. // We create it ahead of time so that any cloning just clones the reference // to the object that will hold any errors. _localDiagnostic.Value = new DiagnosticHolder(); await _next(httpContext); } catch (Exception exception) { try { if (ShouldDisplayErrorPage(exception)) { var contextType = _localDiagnostic.Value.ContextType; var context = (DbContext)httpContext.RequestServices.GetService(contextType); if (context == null) { _logger.LogError(Strings.FormatDatabaseErrorPageMiddleware_ContextNotRegistered(contextType.FullName)); } else { var relationalDatabaseCreator = context.GetService <IDatabaseCreator>() as IRelationalDatabaseCreator; if (relationalDatabaseCreator == null) { _logger.LogDebug(Strings.DatabaseErrorPage_NotRelationalDatabase); } else { var databaseExists = await relationalDatabaseCreator.ExistsAsync(); var migrationsAssembly = context.GetService <IMigrationsAssembly>(); var modelDiffer = context.GetService <IMigrationsModelDiffer>(); // HasDifferences will return true if there is no model snapshot, but if there is an existing database // and no model snapshot then we don't want to show the error page since they are most likely targeting // and existing database and have just misconfigured their model var pendingModelChanges = (!databaseExists || migrationsAssembly.ModelSnapshot != null) && modelDiffer.HasDifferences(migrationsAssembly.ModelSnapshot?.Model, context.Model); var pendingMigrations = (databaseExists ? await context.Database.GetPendingMigrationsAsync() : context.Database.GetMigrations()) .ToArray(); if (pendingModelChanges || pendingMigrations.Any()) { var page = new DatabaseErrorPage { Model = new DatabaseErrorPageModel( contextType, exception, databaseExists, pendingModelChanges, pendingMigrations, _options) }; await page.ExecuteAsync(httpContext); return; } } } } } catch (Exception e) { _logger.LogError( eventId: 0, exception: e, message: Strings.DatabaseErrorPageMiddleware_Exception); } throw; } }