Example #1
0
        /// <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;
            }
        }
Example #2
0
        /// <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;
            }
        }