aspnetcore/src/Microsoft.AspNet.Diagnostic.../DatabaseErrorPageMiddleware.cs

140 lines
5.8 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using Microsoft.AspNet.Diagnostics.Entity.Views;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.RequestContainer;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Data.Entity.Internal;
using Microsoft.Data.Entity.Relational;
using Microsoft.Data.Entity.Relational.Migrations;
using Microsoft.Framework.Logging;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class DatabaseErrorPageMiddleware
{
private readonly RequestDelegate _next;
private readonly DatabaseErrorPageOptions _options;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly DataStoreErrorLoggerProvider _loggerProvider;
public DatabaseErrorPageMiddleware([NotNull] RequestDelegate next, [NotNull] IServiceProvider serviceProvider, [NotNull] ILoggerFactory loggerFactory, [NotNull] DatabaseErrorPageOptions options)
{
Check.NotNull(next, "next");
Check.NotNull(serviceProvider, "serviceProvider");
Check.NotNull(loggerFactory, "loggerFactory");
Check.NotNull(options, "options");
_next = next;
_serviceProvider = serviceProvider;
_options = options;
_logger = loggerFactory.CreateLogger<DatabaseErrorPageMiddleware>();
_loggerProvider = new DataStoreErrorLoggerProvider();
loggerFactory.AddProvider(_loggerProvider);
}
public virtual async Task Invoke([NotNull] HttpContext context)
{
Check.NotNull(context, "context");
try
{
#if !ASPNETCORE50
// 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).WithCurrentCulture();
}
catch (Exception ex)
{
try
{
if (ShouldDisplayErrorPage(_loggerProvider.Logger.LastError, ex, _logger))
{
using (RequestServicesContainer.EnsureRequestServices(context, _serviceProvider))
{
var dbContextType = _loggerProvider.Logger.LastError.ContextType;
var dbContext = (DbContext)context.RequestServices.GetService(dbContextType);
if (dbContext == null)
{
_logger.LogError(Strings.FormatDatabaseErrorPageMiddleware_ContextNotRegistered(dbContextType.FullName));
}
else
{
if (!(dbContext.Database is RelationalDatabase))
{
_logger.LogVerbose(Strings.DatabaseErrorPage_NotRelationalDatabase);
}
else
{
var databaseExists = dbContext.Database.AsRelational().Exists();
var migrator = ((IAccessor<Migrator>)dbContext.Database).Service;
var pendingMigrations = migrator.GetUnappliedMigrations().Select(m => m.Id);
var pendingModelChanges = migrator.HasPendingModelChanges();
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).WithCurrentCulture();
return;
}
}
}
}
}
}
catch (Exception e)
{
_logger.LogError(Strings.DatabaseErrorPageMiddleware_Exception, e);
}
throw;
}
}
private static bool ShouldDisplayErrorPage(DataStoreErrorLogger.DataStoreErrorLog lastError, Exception exception, ILogger logger)
{
logger.LogVerbose(Strings.FormatDatabaseErrorPage_AttemptingToMatchException(exception.GetType()));
if (!lastError.IsErrorLogged)
{
logger.LogVerbose(Strings.DatabaseErrorPage_NoRecordedException);
return false;
}
bool match = false;
for (var e = exception; e != null && !match; e = e.InnerException)
{
match = lastError.Exception == e;
}
if (!match)
{
logger.LogVerbose(Strings.DatabaseErrorPage_NoMatch);
return false;
}
logger.LogVerbose(Strings.DatabaseErrorPage_Matched);
return true;
}
}
}