Convert DatabaseErrorPage to exception filter (#24588)
* Convert DatabaseErrorPage middleware to exception filter
This commit is contained in:
parent
a6abd1101d
commit
cfe158cbed
|
|
@ -40,6 +40,8 @@ namespace ApiAuthSample
|
|||
|
||||
services.AddMvc()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
@ -48,7 +50,6 @@ namespace ApiAuthSample
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ namespace IdentitySample.DefaultUI
|
|||
services.AddDefaultIdentity<ApplicationUser>(o => o.SignIn.RequireConfirmedAccount = true)
|
||||
.AddRoles<IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -58,7 +60,6 @@ namespace IdentitySample.DefaultUI
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ namespace IdentitySample
|
|||
// Add application services.
|
||||
services.AddTransient<IEmailSender, AuthMessageSender>();
|
||||
services.AddTransient<ISmsSender, AuthMessageSender>();
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
@ -61,7 +63,6 @@ namespace IdentitySample
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ namespace Identity.DefaultUI.WebSite
|
|||
options.Conventions.AuthorizePage("/Areas/Identity/Pages/Account/Logout");
|
||||
})
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
@ -47,7 +49,6 @@ namespace Identity.DefaultUI.WebSite
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,9 +49,11 @@ namespace Identity.DefaultUI.WebSite
|
|||
services.AddDefaultIdentity<TUser>()
|
||||
.AddRoles<IdentityRole>()
|
||||
.AddEntityFrameworkStores<TContext>();
|
||||
|
||||
|
||||
services.AddMvc();
|
||||
services.AddSingleton<IFileVersionProvider, FileVersionProvider>();
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
@ -59,11 +61,10 @@ namespace Identity.DefaultUI.WebSite
|
|||
{
|
||||
// This prevents running out of file watchers on some linux machines
|
||||
DisableFilePolling(env);
|
||||
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace Identity.DefaultUI.WebSite
|
|||
{
|
||||
base.ConfigureServices(services);
|
||||
services.AddMvc(options => options.EnableEndpointRouting = false);
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
@ -33,7 +34,6 @@ namespace Identity.DefaultUI.WebSite
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
||||
{
|
||||
internal class DatabaseContextDetails
|
||||
{
|
||||
public Type Type { get; }
|
||||
public bool DatabaseExists { get; }
|
||||
public bool PendingModelChanges { get; }
|
||||
public IEnumerable<string> PendingMigrations { get; }
|
||||
|
||||
public DatabaseContextDetails(Type type, bool databaseExists, bool pendingModelChanges, IEnumerable<string> pendingMigrations)
|
||||
{
|
||||
Type = type;
|
||||
DatabaseExists = databaseExists;
|
||||
PendingModelChanges = pendingModelChanges;
|
||||
PendingMigrations = pendingMigrations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
#nullable enable
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
||||
{
|
||||
public sealed class DatabaseDeveloperPageExceptionFilter : IDeveloperPageExceptionFilter
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly DatabaseErrorPageOptions _options;
|
||||
|
||||
public DatabaseDeveloperPageExceptionFilter(ILogger<DatabaseDeveloperPageExceptionFilter> logger, IOptions<DatabaseErrorPageOptions> options)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public async Task HandleExceptionAsync(ErrorContext errorContext, Func<ErrorContext, Task> next)
|
||||
{
|
||||
if (!(errorContext.Exception is DbException))
|
||||
{
|
||||
await next(errorContext);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Look for DbContext classes registered in the service provider
|
||||
var registeredContexts = errorContext.HttpContext.RequestServices.GetServices<DbContextOptions>()
|
||||
.Select(o => o.ContextType);
|
||||
|
||||
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(errorContext.Exception, contextDetails, _options, errorContext.HttpContext.Request.PathBase)
|
||||
};
|
||||
|
||||
await page.ExecuteAsync(errorContext.HttpContext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.DatabaseErrorPageMiddlewareException(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
#nullable enable
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Service extension methods for the <see cref="DatabaseDeveloperPageExceptionFilter"/>.
|
||||
/// </summary>
|
||||
public static class DatabaseDeveloperPageExceptionFilterServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Add response caching services.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddDatabaseDeveloperPageExceptionFilter(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.TryAddEnumerable(new ServiceDescriptor(typeof(IDeveloperPageExceptionFilter), typeof(DatabaseDeveloperPageExceptionFilter), ServiceLifetime.Singleton));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <summary>
|
||||
/// <see cref="IApplicationBuilder"/> extension methods for the <see cref="DatabaseErrorPageMiddleware"/>.
|
||||
/// </summary>
|
||||
[Obsolete("This is obsolete and will be removed in a future version. Use DatabaseDeveloperPageExceptionFilter instead, see documentation at https://aka.ms/DatabaseDeveloperPageExceptionFilter.")]
|
||||
public static class DatabaseErrorPageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -19,6 +20,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/> to register the middleware with.</param>
|
||||
/// <returns>The same <see cref="IApplicationBuilder"/> instance so that multiple calls can be chained.</returns>
|
||||
[Obsolete("This is obsolete and will be removed in a future version. Use DatabaseDeveloperPageExceptionFilter instead, see documentation at https://aka.ms/DatabaseDeveloperPageExceptionFilter.")]
|
||||
public static IApplicationBuilder UseDatabaseErrorPage(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
|
|
@ -36,6 +38,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <param name="app">The <see cref="IApplicationBuilder"/> to register the middleware with.</param>
|
||||
/// <param name="options">A <see cref="DatabaseErrorPageOptions"/> that specifies options for the middleware.</param>
|
||||
/// <returns>The same <see cref="IApplicationBuilder"/> instance so that multiple calls can be chained.</returns>
|
||||
[Obsolete("This is obsolete and will be removed in a future version. Use DatabaseDeveloperPageExceptionFilter instead, see documentation at https://aka.ms/DatabaseDeveloperPageExceptionFilter.")]
|
||||
public static IApplicationBuilder UseDatabaseErrorPage(
|
||||
this IApplicationBuilder app, DatabaseErrorPageOptions options)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,12 +12,6 @@ using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
@ -56,6 +50,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
/// consumes them to detect database related exception.
|
||||
/// </param>
|
||||
/// <param name="options">The options to control what information is displayed on the error page.</param>
|
||||
[Obsolete("This is obsolete and will be removed in a future version. Use DatabaseDeveloperPageExceptionFilter instead, see documentation at https://aka.ms/DatabaseDeveloperPageExceptionFilter.")]
|
||||
public DatabaseErrorPageMiddleware(
|
||||
RequestDelegate next,
|
||||
ILoggerFactory loggerFactory,
|
||||
|
|
@ -101,7 +96,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
{
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
|
|
@ -116,81 +111,18 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
if (ShouldDisplayErrorPage(exception))
|
||||
{
|
||||
var contextType = _localDiagnostic.Value.ContextType;
|
||||
var context = (DbContext)httpContext.RequestServices.GetService(contextType);
|
||||
var details = await httpContext.GetContextDetailsAsync(contextType, _logger);
|
||||
|
||||
if (context == null)
|
||||
if (details != null && (details.PendingModelChanges || details.PendingMigrations.Count() > 0))
|
||||
{
|
||||
_logger.ContextNotRegisteredDatabaseErrorPageMiddleware(contextType.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var relationalDatabaseCreator = context.GetService<IDatabaseCreator>() as IRelationalDatabaseCreator;
|
||||
if (relationalDatabaseCreator == null)
|
||||
var page = new DatabaseErrorPage
|
||||
{
|
||||
_logger.NotRelationalDatabase();
|
||||
}
|
||||
else
|
||||
{
|
||||
var databaseExists = await relationalDatabaseCreator.ExistsAsync();
|
||||
Model = new DatabaseErrorPageModel(exception, new DatabaseContextDetails[] { details }, _options, httpContext.Request.PathBase)
|
||||
};
|
||||
|
||||
if (databaseExists)
|
||||
{
|
||||
databaseExists = await relationalDatabaseCreator.HasTablesAsync();
|
||||
}
|
||||
await page.ExecuteAsync(httpContext);
|
||||
|
||||
var migrationsAssembly = context.GetService<IMigrationsAssembly>();
|
||||
var modelDiffer = context.GetService<IMigrationsModelDiffer>();
|
||||
|
||||
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
|
||||
if (snapshotModel is IConventionModel conventionModel)
|
||||
{
|
||||
var conventionSet = context.GetService<IConventionSetBuilder>().CreateConventionSet();
|
||||
|
||||
var typeMappingConvention = conventionSet.ModelFinalizingConventions.OfType<TypeMappingConvention>().FirstOrDefault();
|
||||
if (typeMappingConvention != null)
|
||||
{
|
||||
typeMappingConvention.ProcessModelFinalizing(conventionModel.Builder, null);
|
||||
}
|
||||
|
||||
var relationalModelConvention = conventionSet.ModelFinalizedConventions.OfType<RelationalModelConvention>().FirstOrDefault();
|
||||
if (relationalModelConvention != null)
|
||||
{
|
||||
snapshotModel = relationalModelConvention.ProcessModelFinalized(conventionModel);
|
||||
}
|
||||
}
|
||||
|
||||
if (snapshotModel is IMutableModel mutableModel)
|
||||
{
|
||||
snapshotModel = mutableModel.FinalizeModel();
|
||||
}
|
||||
|
||||
// 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(snapshotModel?.GetRelationalModel(), context.Model.GetRelationalModel());
|
||||
|
||||
var pendingMigrations
|
||||
= (databaseExists
|
||||
? await context.Database.GetPendingMigrationsAsync()
|
||||
: context.Database.GetMigrations())
|
||||
.ToArray();
|
||||
|
||||
if (pendingModelChanges || pendingMigrations.Length > 0)
|
||||
{
|
||||
var page = new DatabaseErrorPage
|
||||
{
|
||||
Model = new DatabaseErrorPageModel(
|
||||
contextType, exception, databaseExists, pendingModelChanges, pendingMigrations, _options)
|
||||
};
|
||||
|
||||
await page.ExecuteAsync(httpContext);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
|
@ -14,11 +14,6 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
new EventId(1, "NoContextType"),
|
||||
"No context type was specified. Ensure the form data from the request includes a 'context' value, specifying the context type name to apply migrations for.");
|
||||
|
||||
private static readonly Action<ILogger, string, Exception> _invalidContextType = LoggerMessage.Define<string>(
|
||||
LogLevel.Error,
|
||||
new EventId(2, "InvalidContextType"),
|
||||
"The context type '{ContextTypeName}' could not be loaded. Ensure this is the correct type name for the context you are trying to apply migrations for.");
|
||||
|
||||
private static readonly Action<ILogger, string, Exception> _contextNotRegistered = LoggerMessage.Define<string>(
|
||||
LogLevel.Error,
|
||||
new EventId(3, "ContextNotRegistered"),
|
||||
|
|
@ -85,11 +80,6 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
_noContextType(logger, null);
|
||||
}
|
||||
|
||||
public static void InvalidContextType(this ILogger logger, string contextTypeName)
|
||||
{
|
||||
_invalidContextType(logger, contextTypeName, null);
|
||||
}
|
||||
|
||||
public static void ContextNotRegistered(this ILogger logger, string contextTypeName)
|
||||
{
|
||||
_contextNotRegistered(logger, contextTypeName, null);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
#nullable enable
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
||||
{
|
||||
internal static class HttpContextDatabaseContextDetailsExtensions
|
||||
{
|
||||
public static async ValueTask<DatabaseContextDetails?> GetContextDetailsAsync(this HttpContext httpContext, Type dbcontextType, ILogger logger)
|
||||
{
|
||||
var context = (DbContext?)httpContext.RequestServices.GetService(dbcontextType);
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
logger.ContextNotRegisteredDatabaseErrorPageMiddleware(dbcontextType.FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
var relationalDatabaseCreator = context.GetService<IDatabaseCreator>() as IRelationalDatabaseCreator;
|
||||
if (relationalDatabaseCreator == null)
|
||||
{
|
||||
logger.NotRelationalDatabase();
|
||||
return null;
|
||||
}
|
||||
|
||||
var databaseExists = await relationalDatabaseCreator.ExistsAsync();
|
||||
|
||||
if (databaseExists)
|
||||
{
|
||||
databaseExists = await relationalDatabaseCreator.HasTablesAsync();
|
||||
}
|
||||
|
||||
var migrationsAssembly = context.GetService<IMigrationsAssembly>();
|
||||
var modelDiffer = context.GetService<IMigrationsModelDiffer>();
|
||||
|
||||
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
|
||||
if (snapshotModel is IConventionModel conventionModel)
|
||||
{
|
||||
var conventionSet = context.GetService<IConventionSetBuilder>().CreateConventionSet();
|
||||
|
||||
var typeMappingConvention = conventionSet.ModelFinalizingConventions.OfType<TypeMappingConvention>().FirstOrDefault();
|
||||
if (typeMappingConvention != null)
|
||||
{
|
||||
typeMappingConvention.ProcessModelFinalizing(conventionModel.Builder, null);
|
||||
}
|
||||
|
||||
var relationalModelConvention = conventionSet.ModelFinalizedConventions.OfType<RelationalModelConvention>().FirstOrDefault();
|
||||
if (relationalModelConvention != null)
|
||||
{
|
||||
snapshotModel = relationalModelConvention.ProcessModelFinalized(conventionModel);
|
||||
}
|
||||
}
|
||||
|
||||
if (snapshotModel is IMutableModel mutableModel)
|
||||
{
|
||||
snapshotModel = mutableModel.FinalizeModel();
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
return new DatabaseContextDetails(
|
||||
type: dbcontextType,
|
||||
databaseExists: databaseExists,
|
||||
pendingModelChanges: (!databaseExists || migrationsAssembly.ModelSnapshot != null)
|
||||
&& modelDiffer.HasDifferences(snapshotModel?.GetRelationalModel(), context.Model.GetRelationalModel()),
|
||||
pendingMigrations: databaseExists
|
||||
? await context.Database.GetPendingMigrationsAsync()
|
||||
: context.Database.GetMigrations());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" />
|
||||
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
||||
<Reference Include="Microsoft.EntityFrameworkCore.Relational" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
|
@ -72,9 +74,10 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
|
||||
if (db != null)
|
||||
{
|
||||
var dbName = db.GetType().FullName;
|
||||
try
|
||||
{
|
||||
_logger.ApplyingMigrations(db.GetType().FullName);
|
||||
_logger.ApplyingMigrations(dbName);
|
||||
|
||||
await db.Database.MigrateAsync();
|
||||
|
||||
|
|
@ -82,13 +85,13 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
context.Response.Headers.Add("Pragma", new[] { "no-cache" });
|
||||
context.Response.Headers.Add("Cache-Control", new[] { "no-cache,no-store" });
|
||||
|
||||
_logger.MigrationsApplied(db.GetType().FullName);
|
||||
_logger.MigrationsApplied(dbName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_Exception(db.GetType().FullName) + ex;
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_Exception(dbName) + ex;
|
||||
|
||||
_logger.MigrationsEndPointMiddlewareException(db.GetType().FullName, ex);
|
||||
_logger.MigrationsEndPointMiddlewareException(dbName, ex);
|
||||
|
||||
throw new InvalidOperationException(message, ex);
|
||||
}
|
||||
|
|
@ -114,32 +117,25 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
return null;
|
||||
}
|
||||
|
||||
// Look for DbContext classes registered in the service provider
|
||||
var registeredContexts = context.RequestServices.GetServices<DbContextOptions>()
|
||||
.Select(o => o.ContextType);
|
||||
|
||||
if (!registeredContexts.Any(c => string.Equals(contextTypeName, c.AssemblyQualifiedName)))
|
||||
{
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_ContextNotRegistered(contextTypeName);
|
||||
|
||||
logger.ContextNotRegistered(contextTypeName);
|
||||
|
||||
await WriteErrorToResponse(context.Response, message);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var contextType = Type.GetType(contextTypeName);
|
||||
|
||||
if (contextType == null)
|
||||
{
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_InvalidContextType(contextTypeName);
|
||||
|
||||
logger.InvalidContextType(contextTypeName);
|
||||
|
||||
await WriteErrorToResponse(context.Response, message);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var db = (DbContext)context.RequestServices.GetService(contextType);
|
||||
|
||||
if (db == null)
|
||||
{
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_ContextNotRegistered(contextType.FullName);
|
||||
|
||||
logger.ContextNotRegistered(contextType.FullName);
|
||||
|
||||
await WriteErrorToResponse(context.Response, message);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
|
@ -148,32 +148,29 @@
|
|||
<value>In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_NoDbOrMigrationsTitle" xml:space="preserve">
|
||||
<value>Use migrations to create the database for {0}</value>
|
||||
<value>Use migrations to create the database</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_PendingChangesInfoPMC" xml:space="preserve">
|
||||
<value>In Visual Studio, use the Package Manager Console to scaffold a new migration for these changes and apply them to the database:</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_PendingChangesTitle" xml:space="preserve">
|
||||
<value>There are pending model changes for {0}</value>
|
||||
<value>There are pending model changes</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_PendingMigrationsInfo" xml:space="preserve">
|
||||
<value>There are migrations for {0} that have not been applied to the database</value>
|
||||
<value>There are migrations that have not been applied to the following database(s):</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_PendingMigrationsTitle" xml:space="preserve">
|
||||
<value>Applying existing migrations for {0} may resolve this issue</value>
|
||||
<value>Applying existing migrations may resolve this issue</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_ApplyMigrationsCommandCLI" xml:space="preserve">
|
||||
<value>> dotnet ef database update</value>
|
||||
</data>
|
||||
<data name="MigrationsEndPointMiddleware_ContextNotRegistered" xml:space="preserve">
|
||||
<value>The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped<{0}>() inside the UseServices(...) call in your application startup code.</value>
|
||||
<value>The context type '{0}' was not found in services. This usually means either the context is invalid or it was not registered in services during startup. You probably want to call AddDBContext<>() inside the ConfigureServices(...) call in your application startup code.</value>
|
||||
</data>
|
||||
<data name="MigrationsEndPointMiddleware_Exception" xml:space="preserve">
|
||||
<value>An error occurred while applying the migrations for '{0}'. See InnerException for details.</value>
|
||||
</data>
|
||||
<data name="MigrationsEndPointMiddleware_InvalidContextType" xml:space="preserve">
|
||||
<value>The context type '{0}' could not be loaded. Ensure this is the correct type name for the context you are trying to apply migrations for.</value>
|
||||
</data>
|
||||
<data name="MigrationsEndPointMiddleware_NoContextType" xml:space="preserve">
|
||||
<value>No context type was specified. Ensure the form data from the request includes a 'context' value, specifying the context type name to apply migrations for.</value>
|
||||
</data>
|
||||
|
|
@ -198,4 +195,10 @@
|
|||
<data name="DatabaseErrorPage_HowToApplyFromCLI" xml:space="preserve">
|
||||
<value>Alternatively, you can apply pending migrations from a command prompt at your project directory:</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_NoDbOrMigrationsInfo" xml:space="preserve">
|
||||
<value>A database needs to be created for the following:</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPage_PendingChangesInfo" xml:space="preserve">
|
||||
<value>Pending model changes are detected in the following:</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -4,31 +4,40 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
|
|||
{
|
||||
#line hidden
|
||||
using System.Threading.Tasks;
|
||||
#nullable restore
|
||||
#line 1 "DatabaseErrorPage.cshtml"
|
||||
using System;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
#nullable restore
|
||||
#line 2 "DatabaseErrorPage.cshtml"
|
||||
using System.Linq;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
#nullable restore
|
||||
#line 3 "DatabaseErrorPage.cshtml"
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
#nullable restore
|
||||
#line 4 "DatabaseErrorPage.cshtml"
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
internal class DatabaseErrorPage : Microsoft.Extensions.RazorViews.BaseView
|
||||
{
|
||||
#pragma warning disable 1998
|
||||
public async override global::System.Threading.Tasks.Task ExecuteAsync()
|
||||
{
|
||||
#nullable restore
|
||||
#line 5 "DatabaseErrorPage.cshtml"
|
||||
|
||||
Response.StatusCode = 500;
|
||||
|
|
@ -37,6 +46,7 @@ using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
|||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(@"<!DOCTYPE html>
|
||||
|
||||
<html lang=""en"" xmlns=""http://www.w3.org/1999/xhtml"">
|
||||
|
|
@ -128,156 +138,265 @@ body .titleerror {
|
|||
</head>
|
||||
<body>
|
||||
<h1>");
|
||||
#nullable restore
|
||||
#line 113 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_Title);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</h1>\r\n <p>\r\n");
|
||||
#nullable restore
|
||||
#line 115 "DatabaseErrorPage.cshtml"
|
||||
for (Exception ex = Model.Exception; ex != null; ex = ex.InnerException)
|
||||
{
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" <span>");
|
||||
#nullable restore
|
||||
#line 117 "DatabaseErrorPage.cshtml"
|
||||
Write(ex.GetType().Name);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(": ");
|
||||
#nullable restore
|
||||
#line 117 "DatabaseErrorPage.cshtml"
|
||||
Write(ex.Message);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</span>\r\n <br />\r\n");
|
||||
#nullable restore
|
||||
#line 119 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" </p>\r\n <hr />\r\n\r\n");
|
||||
#nullable restore
|
||||
#line 123 "DatabaseErrorPage.cshtml"
|
||||
if (!Model.DatabaseExists && !Model.PendingMigrations.Any())
|
||||
{
|
||||
|
||||
var contextWithNoDBOrMigrations = Model.ContextDetails.Where(c => !c.DatabaseExists && !c.PendingMigrations.Any());
|
||||
if (contextWithNoDBOrMigrations.Any())
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <h2>");
|
||||
#line 125 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.FormatDatabaseErrorPage_NoDbOrMigrationsTitle(Model.ContextType.Name));
|
||||
#nullable disable
|
||||
WriteLiteral(" <div>\r\n <h2>");
|
||||
#nullable restore
|
||||
#line 128 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsTitle);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</h2>\r\n <p>");
|
||||
#line 126 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfoPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n <code> ");
|
||||
#line 127 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code> ");
|
||||
#nullable disable
|
||||
WriteLiteral("</h2>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 129 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
|
||||
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfo);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <p>");
|
||||
#line 130 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfoCLI);
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n\r\n <ul>\r\n");
|
||||
#nullable restore
|
||||
#line 132 "DatabaseErrorPage.cshtml"
|
||||
foreach (var context in contextWithNoDBOrMigrations)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n <code> ");
|
||||
#line 131 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandCLI);
|
||||
#nullable disable
|
||||
WriteLiteral(" <li>");
|
||||
#nullable restore
|
||||
#line 134 "DatabaseErrorPage.cshtml"
|
||||
Write(context.Type.Name);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code> ");
|
||||
#line 133 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <hr />\r\n");
|
||||
#nullable disable
|
||||
WriteLiteral("</li>\r\n");
|
||||
#nullable restore
|
||||
#line 135 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
else if (Model.PendingMigrations.Any())
|
||||
{
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div>\r\n <h2>");
|
||||
#nullable disable
|
||||
WriteLiteral(" </ul>\r\n\r\n <p>");
|
||||
#nullable restore
|
||||
#line 138 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfoPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <code> ");
|
||||
#nullable restore
|
||||
#line 139 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.FormatDatabaseErrorPage_PendingMigrationsTitle(Model.ContextType.Name));
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</h2>\r\n <p>");
|
||||
#line 140 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.FormatDatabaseErrorPage_PendingMigrationsInfo(Model.ContextType.Name));
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code> ");
|
||||
#nullable restore
|
||||
#line 141 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n\r\n <ul>\r\n");
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 142 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfoCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <code> ");
|
||||
#nullable restore
|
||||
#line 143 "DatabaseErrorPage.cshtml"
|
||||
foreach (var migration in Model.PendingMigrations)
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code> ");
|
||||
#nullable restore
|
||||
#line 145 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
|
||||
#nullable restore
|
||||
#line 148 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
var contextWithPendingMigrations = Model.ContextDetails.Where(c => c.PendingMigrations.Any()).Except(contextWithNoDBOrMigrations);
|
||||
if (contextWithPendingMigrations.Any())
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" <div>\r\n <h2>");
|
||||
#nullable restore
|
||||
#line 154 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingMigrationsTitle);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</h2>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 155 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingMigrationsInfo);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n\r\n");
|
||||
#nullable restore
|
||||
#line 157 "DatabaseErrorPage.cshtml"
|
||||
foreach (var context in contextWithPendingMigrations)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <li>");
|
||||
#line 145 "DatabaseErrorPage.cshtml"
|
||||
Write(migration);
|
||||
#nullable disable
|
||||
WriteLiteral(" <h3>");
|
||||
#nullable restore
|
||||
#line 159 "DatabaseErrorPage.cshtml"
|
||||
Write(context.Type.Name);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</h3>\r\n <ul>\r\n");
|
||||
#nullable restore
|
||||
#line 161 "DatabaseErrorPage.cshtml"
|
||||
foreach (var migration in context.PendingMigrations)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" <li>");
|
||||
#nullable restore
|
||||
#line 163 "DatabaseErrorPage.cshtml"
|
||||
Write(migration);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</li>\r\n");
|
||||
#line 146 "DatabaseErrorPage.cshtml"
|
||||
#nullable restore
|
||||
#line 164 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" </ul>\r\n");
|
||||
WriteLiteral(" <p>\r\n <button id=\"applyMigrations\" onclick=\"ApplyMigrations()\" data-assemblyname=\"");
|
||||
#nullable restore
|
||||
#line 168 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(context.Type.AssemblyQualifiedName));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("\">");
|
||||
#nullable restore
|
||||
#line 168 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsButton);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</button>\r\n <span id=\"applyMigrationsError\" class=\"error\"></span>\r\n <span id=\"applyMigrationsSuccess\"></span>\r\n </p>\r\n");
|
||||
#nullable restore
|
||||
#line 172 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </ul>\r\n\r\n <p>\r\n <button id=\"applyMigrations\" onclick=\"ApplyMigrations()\">");
|
||||
#line 150 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsButton);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@"</button>
|
||||
<span id=""applyMigrationsError"" class=""error""></span>
|
||||
<span id=""applyMigrationsSuccess""></span>
|
||||
</p>
|
||||
<script>
|
||||
function ApplyMigrations() {
|
||||
applyMigrations.disabled = true;
|
||||
applyMigrationsError.innerHTML = """";
|
||||
applyMigrations.innerHTML = """);
|
||||
#line 158 "DatabaseErrorPage.cshtml"
|
||||
#nullable disable
|
||||
WriteLiteral("\r\n <script>\r\n function ApplyMigrations() {\r\n applyMigrations.disabled = true;\r\n applyMigrationsError.innerHTML = \"\";\r\n applyMigrations.innerHTML = \"");
|
||||
#nullable restore
|
||||
#line 178 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButtonRunning));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("\";\r\n\r\n var req = new XMLHttpRequest();\r\n\r\n req.onload = function (e) {\r\n if (req.status === 204) {\r\n applyMigrations.innerHTML = \"");
|
||||
#line 164 "DatabaseErrorPage.cshtml"
|
||||
#nullable restore
|
||||
#line 184 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButtonDone));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("\";\r\n applyMigrationsSuccess.innerHTML = \"");
|
||||
#line 165 "DatabaseErrorPage.cshtml"
|
||||
#nullable restore
|
||||
#line 185 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Strings.DatabaseErrorPage_MigrationsAppliedRefresh));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(@""";
|
||||
} else {
|
||||
ErrorApplyingMigrations();
|
||||
|
|
@ -288,18 +407,15 @@ body .titleerror {
|
|||
ErrorApplyingMigrations();
|
||||
};
|
||||
|
||||
var formBody = ""context=");
|
||||
#line 175 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(UrlEncode(Model.ContextType.AssemblyQualifiedName)));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\";\r\n req.open(\"POST\", \"");
|
||||
#line 176 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Model.Options.MigrationsEndPointPath.Value));
|
||||
var formBody = ""context="" + encodeURIComponent(document.getElementById('applyMigrations').getAttribute('data-assemblyname'));
|
||||
req.open(""POST"", """);
|
||||
#nullable restore
|
||||
#line 196 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Model.PathBase.Add(Model.Options.MigrationsEndPointPath).Value));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(@""", true);
|
||||
req.setRequestHeader(""Content-type"", ""application/x-www-form-urlencoded"");
|
||||
req.send(formBody);
|
||||
|
|
@ -307,100 +423,167 @@ body .titleerror {
|
|||
|
||||
function ErrorApplyingMigrations() {
|
||||
applyMigrations.innerHTML = """);
|
||||
#line 182 "DatabaseErrorPage.cshtml"
|
||||
#nullable restore
|
||||
#line 202 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButton));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("\";\r\n applyMigrationsError.innerHTML = \"");
|
||||
#line 183 "DatabaseErrorPage.cshtml"
|
||||
#nullable restore
|
||||
#line 203 "DatabaseErrorPage.cshtml"
|
||||
Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsFailed));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\";\r\n applyMigrations.disabled = false;\r\n }\r\n </script>\r\n\r\n <p>");
|
||||
#line 188 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_HowToApplyFromPMC);
|
||||
#nullable disable
|
||||
WriteLiteral("\";\r\n applyMigrations.disabled = false;\r\n }\r\n </script>\r\n\r\n <p>");
|
||||
#nullable restore
|
||||
#line 208 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_HowToApplyFromPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#line 189 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <p>");
|
||||
#line 190 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_HowToApplyFromCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#line 191 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
|
||||
#line 194 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
else if (Model.PendingModelChanges)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div>\r\n <h2>");
|
||||
#line 198 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.FormatDatabaseErrorPage_PendingChangesTitle(Model.ContextType.Name));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</h2>\r\n <p>");
|
||||
#line 199 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingChangesInfoPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#line 200 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code>");
|
||||
#line 202 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <p>");
|
||||
#line 203 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingChangesInfoCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#line 204 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code>");
|
||||
#line 206 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#nullable restore
|
||||
#line 209 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</body>\r\n</html>");
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 210 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_HowToApplyFromCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#nullable restore
|
||||
#line 211 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
|
||||
#nullable restore
|
||||
#line 214 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
var contextWithPendingModelChanges = Model.ContextDetails.Where(c => c.PendingModelChanges).Except(contextWithNoDBOrMigrations).Except(contextWithPendingMigrations);
|
||||
if (contextWithPendingModelChanges.Any())
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" <div>\r\n <h2>");
|
||||
#nullable restore
|
||||
#line 220 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingChangesTitle);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</h2>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 221 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingChangesInfo);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <ul>\r\n");
|
||||
#nullable restore
|
||||
#line 223 "DatabaseErrorPage.cshtml"
|
||||
foreach (var context in contextWithPendingModelChanges)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" <li>");
|
||||
#nullable restore
|
||||
#line 225 "DatabaseErrorPage.cshtml"
|
||||
Write(context.Type.Name);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</li>\r\n");
|
||||
#nullable restore
|
||||
#line 226 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral(" </ul>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 228 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingChangesInfoPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#nullable restore
|
||||
#line 229 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code>");
|
||||
#nullable restore
|
||||
#line 231 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <p>");
|
||||
#nullable restore
|
||||
#line 232 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_PendingChangesInfoCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</p>\r\n <code>");
|
||||
#nullable restore
|
||||
#line 233 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_AddMigrationCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <br />\r\n <code>");
|
||||
#nullable restore
|
||||
#line 235 "DatabaseErrorPage.cshtml"
|
||||
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
|
||||
#nullable restore
|
||||
#line 238 "DatabaseErrorPage.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
WriteLiteral("</body>\r\n</html>\r\n");
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
#nullable restore
|
||||
#line 11 "DatabaseErrorPage.cshtml"
|
||||
|
||||
public DatabaseErrorPageModel Model { get; set; }
|
||||
|
|
@ -417,6 +600,7 @@ body .titleerror {
|
|||
|
||||
#line default
|
||||
#line hidden
|
||||
#nullable disable
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
|
|
|
|||
|
|
@ -35,45 +35,65 @@
|
|||
<h1>@Strings.DatabaseErrorPage_Title</h1>
|
||||
<p>
|
||||
@for (Exception ex = Model.Exception; ex != null; ex = ex.InnerException)
|
||||
{
|
||||
{
|
||||
<span>@ex.GetType().Name: @ex.Message</span>
|
||||
<br />
|
||||
}
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
@if (!Model.DatabaseExists && !Model.PendingMigrations.Any())
|
||||
{
|
||||
<h2>@Strings.FormatDatabaseErrorPage_NoDbOrMigrationsTitle(Model.ContextType.Name)</h2>
|
||||
<p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfoPMC</p>
|
||||
<code> @Strings.DatabaseErrorPage_AddMigrationCommandPMC</code>
|
||||
<br />
|
||||
<code> @Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
|
||||
<p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfoCLI</p>
|
||||
<code> @Strings.DatabaseErrorPage_AddMigrationCommandCLI</code>
|
||||
<br />
|
||||
<code> @Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
|
||||
<hr />
|
||||
}
|
||||
else if (Model.PendingMigrations.Any())
|
||||
{
|
||||
<div>
|
||||
<h2>@Strings.FormatDatabaseErrorPage_PendingMigrationsTitle(Model.ContextType.Name)</h2>
|
||||
<p>@Strings.FormatDatabaseErrorPage_PendingMigrationsInfo(Model.ContextType.Name)</p>
|
||||
@{
|
||||
var contextWithNoDBOrMigrations = Model.ContextDetails.Where(c => !c.DatabaseExists && !c.PendingMigrations.Any());
|
||||
if (contextWithNoDBOrMigrations.Any())
|
||||
{
|
||||
<div>
|
||||
<h2>@Strings.DatabaseErrorPage_NoDbOrMigrationsTitle</h2>
|
||||
<p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfo</p>
|
||||
|
||||
<ul>
|
||||
@foreach (var migration in Model.PendingMigrations)
|
||||
<ul>
|
||||
@foreach (var context in contextWithNoDBOrMigrations)
|
||||
{
|
||||
<li>@context.Type.Name</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
<p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfoPMC</p>
|
||||
<code> @Strings.DatabaseErrorPage_AddMigrationCommandPMC</code>
|
||||
<br />
|
||||
<code> @Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
|
||||
<p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfoCLI</p>
|
||||
<code> @Strings.DatabaseErrorPage_AddMigrationCommandCLI</code>
|
||||
<br />
|
||||
<code> @Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
|
||||
<hr />
|
||||
</div>
|
||||
}
|
||||
|
||||
var contextWithPendingMigrations = Model.ContextDetails.Where(c => c.PendingMigrations.Any()).Except(contextWithNoDBOrMigrations);
|
||||
if (contextWithPendingMigrations.Any())
|
||||
{
|
||||
<div>
|
||||
<h2>@Strings.DatabaseErrorPage_PendingMigrationsTitle</h2>
|
||||
<p>@Strings.DatabaseErrorPage_PendingMigrationsInfo</p>
|
||||
|
||||
@foreach (var context in contextWithPendingMigrations)
|
||||
{
|
||||
<li>@migration</li>
|
||||
}
|
||||
</ul>
|
||||
<h3>@context.Type.Name</h3>
|
||||
<ul>
|
||||
@foreach (var migration in context.PendingMigrations)
|
||||
{
|
||||
<li>@migration</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<button id="applyMigrations" onclick="ApplyMigrations()">@Strings.DatabaseErrorPage_ApplyMigrationsButton</button>
|
||||
<span id="applyMigrationsError" class="error"></span>
|
||||
<span id="applyMigrationsSuccess"></span>
|
||||
</p>
|
||||
<script>
|
||||
<p>
|
||||
<button id="applyMigrations" onclick="ApplyMigrations()" data-assemblyname="@JavaScriptEncode(context.Type.AssemblyQualifiedName)">@Strings.DatabaseErrorPage_ApplyMigrationsButton</button>
|
||||
<span id="applyMigrationsError" class="error"></span>
|
||||
<span id="applyMigrationsSuccess"></span>
|
||||
</p>
|
||||
}
|
||||
|
||||
<script>
|
||||
function ApplyMigrations() {
|
||||
applyMigrations.disabled = true;
|
||||
applyMigrationsError.innerHTML = "";
|
||||
|
|
@ -94,8 +114,8 @@
|
|||
ErrorApplyingMigrations();
|
||||
};
|
||||
|
||||
var formBody = "context=@JavaScriptEncode(UrlEncode(Model.ContextType.AssemblyQualifiedName))";
|
||||
req.open("POST", "@JavaScriptEncode(Model.Options.MigrationsEndPointPath.Value)", true);
|
||||
var formBody = "context=" + encodeURIComponent(document.getElementById('applyMigrations').getAttribute('data-assemblyname'));
|
||||
req.open("POST", "@JavaScriptEncode(Model.PathBase.Add(Model.Options.MigrationsEndPointPath).Value)", true);
|
||||
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
req.send(formBody);
|
||||
}
|
||||
|
|
@ -105,29 +125,39 @@
|
|||
applyMigrationsError.innerHTML = "@JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsFailed)";
|
||||
applyMigrations.disabled = false;
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<p>@Strings.DatabaseErrorPage_HowToApplyFromPMC</p>
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
|
||||
<p>@Strings.DatabaseErrorPage_HowToApplyFromCLI</p>
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
|
||||
<hr />
|
||||
</div>
|
||||
}
|
||||
else if (Model.PendingModelChanges)
|
||||
{
|
||||
<div>
|
||||
<h2>@Strings.FormatDatabaseErrorPage_PendingChangesTitle(Model.ContextType.Name)</h2>
|
||||
<p>@Strings.DatabaseErrorPage_PendingChangesInfoPMC</p>
|
||||
<code>@Strings.DatabaseErrorPage_AddMigrationCommandPMC</code>
|
||||
<br />
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
|
||||
<p>@Strings.DatabaseErrorPage_PendingChangesInfoCLI</p>
|
||||
<code>@Strings.DatabaseErrorPage_AddMigrationCommandCLI</code>
|
||||
<br />
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
|
||||
<hr />
|
||||
</div>
|
||||
<p>@Strings.DatabaseErrorPage_HowToApplyFromPMC</p>
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
|
||||
<p>@Strings.DatabaseErrorPage_HowToApplyFromCLI</p>
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
|
||||
<hr />
|
||||
</div>
|
||||
}
|
||||
|
||||
var contextWithPendingModelChanges = Model.ContextDetails.Where(c => c.PendingModelChanges).Except(contextWithNoDBOrMigrations).Except(contextWithPendingMigrations);
|
||||
if (contextWithPendingModelChanges.Any())
|
||||
{
|
||||
<div>
|
||||
<h2>@Strings.DatabaseErrorPage_PendingChangesTitle</h2>
|
||||
<p>@Strings.DatabaseErrorPage_PendingChangesInfo</p>
|
||||
<ul>
|
||||
@foreach (var context in contextWithPendingModelChanges)
|
||||
{
|
||||
<li>@context.Type.Name</li>
|
||||
}
|
||||
</ul>
|
||||
<p>@Strings.DatabaseErrorPage_PendingChangesInfoPMC</p>
|
||||
<code>@Strings.DatabaseErrorPage_AddMigrationCommandPMC</code>
|
||||
<br />
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
|
||||
<p>@Strings.DatabaseErrorPage_PendingChangesInfoCLI</p>
|
||||
<code>@Strings.DatabaseErrorPage_AddMigrationCommandCLI</code>
|
||||
<br />
|
||||
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
|
||||
<hr />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,32 +4,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
|
||||
{
|
||||
internal class DatabaseErrorPageModel
|
||||
{
|
||||
public DatabaseErrorPageModel(
|
||||
Type contextType,
|
||||
Exception exception,
|
||||
bool databaseExists,
|
||||
bool pendingModelChanges,
|
||||
IEnumerable<string> pendingMigrations,
|
||||
DatabaseErrorPageOptions options)
|
||||
IEnumerable<DatabaseContextDetails> contextDetails,
|
||||
DatabaseErrorPageOptions options,
|
||||
PathString pathBase)
|
||||
{
|
||||
ContextType = contextType;
|
||||
Exception = exception;
|
||||
DatabaseExists = databaseExists;
|
||||
PendingModelChanges = pendingModelChanges;
|
||||
PendingMigrations = pendingMigrations;
|
||||
ContextDetails = contextDetails;
|
||||
Options = options;
|
||||
PathBase = pathBase;
|
||||
}
|
||||
|
||||
public virtual Type ContextType { get; }
|
||||
public virtual Exception Exception { get; }
|
||||
public virtual bool DatabaseExists { get; }
|
||||
public virtual bool PendingModelChanges { get; }
|
||||
public virtual IEnumerable<string> PendingMigrations { get; }
|
||||
public virtual IEnumerable<DatabaseContextDetails> ContextDetails { get; }
|
||||
public virtual DatabaseErrorPageOptions Options { get; }
|
||||
public virtual PathString PathBase { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
{
|
||||
webHostBuilder
|
||||
.UseTestServer()
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
.Configure(app => app
|
||||
.UseDatabaseErrorPage()
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
.UseMiddleware<SuccessMiddleware>());
|
||||
}).Build();
|
||||
|
||||
|
|
@ -68,8 +70,10 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
{
|
||||
webHostBuilder
|
||||
.UseTestServer()
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
.Configure(app => app
|
||||
.UseDatabaseErrorPage()
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
.UseMiddleware<ExceptionMiddleware>());
|
||||
}).Build();
|
||||
|
||||
|
|
@ -140,7 +144,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
|
||||
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", typeof(BloggingContext).Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsInfo"), content);
|
||||
Assert.Contains(typeof(BloggingContext).Name, content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_AddMigrationCommandPMC").Replace(">", ">"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_ApplyMigrationsCommandPMC").Replace(">", ">"), content);
|
||||
}
|
||||
|
|
@ -200,7 +206,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", typeof(BloggingContextWithMigrations).Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingMigrationsTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingMigrationsInfo"), content);
|
||||
Assert.Contains(typeof(BloggingContextWithMigrations).Name, content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_ApplyMigrationsCommandPMC").Replace(">", ">"), content);
|
||||
Assert.Contains("<li>111111111111111_MigrationOne</li>", content);
|
||||
Assert.Contains("<li>222222222222222_MigrationTwo</li>", content);
|
||||
|
|
@ -237,7 +245,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", typeof(BloggingContextWithPendingModelChanges).Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingChangesTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingChangesInfo"), content);
|
||||
Assert.Contains(typeof(BloggingContextWithPendingModelChanges).Name, content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_AddMigrationCommandCLI").Replace(">", ">"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_AddMigrationCommandPMC").Replace(">", ">"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_ApplyMigrationsCommandCLI").Replace(">", ">"), content);
|
||||
|
|
@ -283,7 +293,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
// Ensure the url we're going to test is what the page is using in it's JavaScript
|
||||
var javaScriptEncoder = JavaScriptEncoder.Default;
|
||||
Assert.Contains("req.open(\"POST\", \"" + JavaScriptEncode(expectedMigrationsEndpoint) + "\", true);", content);
|
||||
Assert.Contains("var formBody = \"context=" + JavaScriptEncode(UrlEncode(expectedContextType)) + "\";", content);
|
||||
Assert.Contains("data-assemblyname=\"" + JavaScriptEncode(expectedContextType) + "\"", content);
|
||||
|
||||
// Step Two: Request to migrations endpoint
|
||||
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
|
||||
|
|
@ -333,10 +343,12 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
.UseTestServer()
|
||||
.Configure(app =>
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
app.UseDatabaseErrorPage(new DatabaseErrorPageOptions
|
||||
{
|
||||
MigrationsEndPointPath = new PathString(migrationsEndpoint)
|
||||
});
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
app.UseMiddleware<PendingMigrationsMiddleware>();
|
||||
})
|
||||
|
|
@ -374,7 +386,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
.UseTestServer()
|
||||
.Configure(app =>
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
app.UseDatabaseErrorPage();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
app.UseMiddleware<ContextNotRegisteredInServicesMiddleware>();
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
app.ApplicationServices.GetService<ILoggerFactory>().AddProvider(logProvider);
|
||||
|
|
@ -478,7 +492,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
Assert.Contains("I wrapped your exception", content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", typeof(BloggingContext).Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsInfo"), content);
|
||||
Assert.Contains(typeof(BloggingContext).Name, content);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -513,7 +529,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
.UseTestServer()
|
||||
.Configure(app =>
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
app.UseDatabaseErrorPage();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
app.UseMiddleware<TMiddleware>();
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_InvalidContextType", typeName), content);
|
||||
Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_ContextNotRegistered", typeName), content);
|
||||
Assert.True(content.Length > 512);
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_ContextNotRegistered", typeof(BloggingContext)), content);
|
||||
Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_ContextNotRegistered", typeof(BloggingContext).AssemblyQualifiedName), content);
|
||||
Assert.True(content.Length > 512);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
#nullable enable
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
||||
{
|
||||
public class DatabaseErrorPageTest
|
||||
|
|
@ -23,12 +24,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception(),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { },
|
||||
options: options);
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -43,12 +49,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception(),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -63,12 +74,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -83,12 +99,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: true,
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: true,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -103,12 +124,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: true,
|
||||
pendingMigrations: new string[] { },
|
||||
options: options);
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: true,
|
||||
pendingMigrations: new string[] { })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -123,12 +149,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception("Something bad happened"),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { },
|
||||
options: options);
|
||||
new Exception("Something bad happened"),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -141,12 +172,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
var options = new DatabaseErrorPageOptions();
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception("Something bad happened", new Exception("Because something more badder happened")),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { },
|
||||
options: options);
|
||||
new Exception("Something bad happened", new Exception("Because something more badder happened")),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
|
|
@ -161,18 +197,46 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
options.MigrationsEndPointPath = "/HitThisEndPoint";
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
contextType: typeof(BloggingContext),
|
||||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" })
|
||||
},
|
||||
options: options,
|
||||
pathBase: PathString.Empty);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
Assert.Contains(options.MigrationsEndPointPath.Value, content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PathBase_is_respected()
|
||||
{
|
||||
var options = new DatabaseErrorPageOptions();
|
||||
options.MigrationsEndPointPath = "/HitThisEndPoint";
|
||||
|
||||
var model = new DatabaseErrorPageModel(
|
||||
new Exception(),
|
||||
new DatabaseContextDetails[]
|
||||
{
|
||||
new DatabaseContextDetails(
|
||||
type: typeof(BloggingContext),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" })
|
||||
},
|
||||
options: options,
|
||||
pathBase: "/PathBase");
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
||||
Assert.Contains("/PathBase/HitThisEndPoint", content);
|
||||
}
|
||||
|
||||
private static async Task<string> ExecutePage(DatabaseErrorPageOptions options, DatabaseErrorPageModel model)
|
||||
{
|
||||
|
|
@ -196,4 +260,4 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,32 +10,38 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers
|
|||
{
|
||||
public static void DisplaysScaffoldFirstMigration(Type contextType, string content)
|
||||
{
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", contextType.Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsInfo"), content);
|
||||
}
|
||||
|
||||
public static void NotDisplaysScaffoldFirstMigration(Type contextType, string content)
|
||||
{
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", contextType.Name), content);
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsTitle"), content);
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("DatabaseErrorPage_NoDbOrMigrationsInfo"), content);
|
||||
}
|
||||
|
||||
public static void DisplaysApplyMigrations(Type contextType, string content)
|
||||
{
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", contextType.Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingMigrationsTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingMigrationsInfo"), content);
|
||||
}
|
||||
|
||||
public static void NotDisplaysApplyMigrations(Type contextType, string content)
|
||||
{
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", contextType.Name), content);
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingMigrationsTitle"), content);
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingMigrationsInfo"), content);
|
||||
}
|
||||
|
||||
public static void DisplaysScaffoldNextMigraion(Type contextType, string content)
|
||||
{
|
||||
Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", contextType.Name), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingChangesTitle"), content);
|
||||
Assert.Contains(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingChangesInfo"), content);
|
||||
}
|
||||
|
||||
public static void NotDisplaysScaffoldNextMigraion(Type contextType, string content)
|
||||
{
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", contextType.Name), content);
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingChangesTitle"), content);
|
||||
Assert.DoesNotContain(StringsHelpers.GetResourceString("DatabaseErrorPage_PendingChangesInfo"), content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,13 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers
|
|||
public static string GetResourceString(string stringName, params object[] parameters)
|
||||
{
|
||||
var strings = typeof(DatabaseErrorPageMiddleware).GetTypeInfo().Assembly.GetType("Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Strings").GetTypeInfo();
|
||||
var method = strings.GetDeclaredMethods(stringName).Single();
|
||||
return (string)method.Invoke(null, parameters);
|
||||
var method = strings.GetDeclaredMethods(stringName).SingleOrDefault();
|
||||
if (method != null)
|
||||
{
|
||||
return (string)method.Invoke(null, parameters);
|
||||
}
|
||||
var property = strings.GetDeclaredProperty(stringName);
|
||||
return (string)property.GetValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ namespace DatabaseErrorPageSample
|
|||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
app.UseDatabaseErrorPage();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
app.Run(context =>
|
||||
{
|
||||
context.RequestServices.GetService<MyContext>().Blog.FirstOrDefault();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
|
||||
namespace RazorPageGenerator
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (args == null || args.Length < 1)
|
||||
{
|
||||
Console.WriteLine("Invalid argument(s).");
|
||||
Console.WriteLine(@"Usage:
|
||||
dotnet razorpagegenerator <root-namespace-of-views> [path]
|
||||
Examples:
|
||||
dotnet razorpagegenerator Microsoft.AspNetCore.Diagnostics.RazorViews
|
||||
- processes all views in ""Views"" subfolders of the current directory
|
||||
dotnet razorpagegenerator Microsoft.AspNetCore.Diagnostics.RazorViews c:\project
|
||||
- processes all views in ""Views"" subfolders of c:\project directory
|
||||
");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
var rootNamespace = args[0];
|
||||
var targetProjectDirectory = args.Length > 1 ? args[1] : Directory.GetCurrentDirectory();
|
||||
var projectEngine = CreateProjectEngine(rootNamespace, targetProjectDirectory);
|
||||
var results = MainCore(projectEngine, targetProjectDirectory);
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
File.WriteAllText(result.FilePath, result.GeneratedCode);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"{results.Count} files successfully generated.");
|
||||
Console.WriteLine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static RazorProjectEngine CreateProjectEngine(string rootNamespace, string targetProjectDirectory, Action<RazorProjectEngineBuilder> configure = null)
|
||||
{
|
||||
var fileSystem = RazorProjectFileSystem.Create(targetProjectDirectory);
|
||||
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder =>
|
||||
{
|
||||
builder
|
||||
.SetNamespace(rootNamespace)
|
||||
.SetBaseType("Microsoft.Extensions.RazorViews.BaseView")
|
||||
.ConfigureClass((document, @class) =>
|
||||
{
|
||||
@class.ClassName = Path.GetFileNameWithoutExtension(document.Source.FilePath);
|
||||
@class.Modifiers.Clear();
|
||||
@class.Modifiers.Add("internal");
|
||||
});
|
||||
|
||||
SectionDirective.Register(builder);
|
||||
|
||||
builder.Features.Add(new SuppressChecksumOptionsFeature());
|
||||
builder.Features.Add(new SuppressMetadataAttributesFeature());
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
configure(builder);
|
||||
}
|
||||
|
||||
builder.AddDefaultImports(@"
|
||||
@using System
|
||||
@using System.Threading.Tasks
|
||||
");
|
||||
});
|
||||
return projectEngine;
|
||||
}
|
||||
|
||||
public static IList<RazorPageGeneratorResult> MainCore(RazorProjectEngine projectEngine, string targetProjectDirectory)
|
||||
{
|
||||
var viewDirectories = Directory.EnumerateDirectories(targetProjectDirectory, "Views", SearchOption.AllDirectories);
|
||||
var fileCount = 0;
|
||||
|
||||
var results = new List<RazorPageGeneratorResult>();
|
||||
foreach (var viewDir in viewDirectories)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(" Generating code files for views in {0}", viewDir);
|
||||
var viewDirPath = viewDir.Substring(targetProjectDirectory.Length).Replace('\\', '/');
|
||||
var cshtmlFiles = projectEngine.FileSystem.EnumerateItems(viewDirPath);
|
||||
|
||||
if (!cshtmlFiles.Any())
|
||||
{
|
||||
Console.WriteLine(" No .cshtml files were found.");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var item in cshtmlFiles)
|
||||
{
|
||||
Console.WriteLine(" Generating code file for view {0}...", item.FileName);
|
||||
results.Add(GenerateCodeFile(projectEngine, item));
|
||||
Console.WriteLine(" Done!");
|
||||
fileCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static RazorPageGeneratorResult GenerateCodeFile(RazorProjectEngine projectEngine, RazorProjectItem projectItem)
|
||||
{
|
||||
var projectItemWrapper = new FileSystemRazorProjectItemWrapper(projectItem);
|
||||
var codeDocument = projectEngine.Process(projectItemWrapper);
|
||||
var cSharpDocument = codeDocument.GetCSharpDocument();
|
||||
if (cSharpDocument.Diagnostics.Any())
|
||||
{
|
||||
var diagnostics = string.Join(Environment.NewLine, cSharpDocument.Diagnostics);
|
||||
Console.WriteLine($"One or more parse errors encountered. This will not prevent the generator from continuing: {Environment.NewLine}{diagnostics}.");
|
||||
}
|
||||
|
||||
var generatedCodeFilePath = Path.ChangeExtension(projectItem.PhysicalPath, ".Designer.cs");
|
||||
return new RazorPageGeneratorResult
|
||||
{
|
||||
FilePath = generatedCodeFilePath,
|
||||
GeneratedCode = cSharpDocument.GeneratedCode,
|
||||
};
|
||||
}
|
||||
|
||||
private class SuppressChecksumOptionsFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature
|
||||
{
|
||||
public int Order { get; set; }
|
||||
|
||||
public void Configure(RazorCodeGenerationOptionsBuilder options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
options.SuppressChecksum = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class SuppressMetadataAttributesFeature : RazorEngineFeatureBase, IConfigureRazorCodeGenerationOptionsFeature
|
||||
{
|
||||
public int Order { get; set; }
|
||||
|
||||
public void Configure(RazorCodeGenerationOptionsBuilder options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
options.SuppressMetadataAttributes = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class FileSystemRazorProjectItemWrapper : RazorProjectItem
|
||||
{
|
||||
private readonly RazorProjectItem _source;
|
||||
|
||||
public FileSystemRazorProjectItemWrapper(RazorProjectItem item)
|
||||
{
|
||||
_source = item;
|
||||
}
|
||||
|
||||
public override string BasePath => _source.BasePath;
|
||||
|
||||
public override string FilePath => _source.FilePath;
|
||||
|
||||
// Mask the full name since we don't want a developer's local file paths to be commited.
|
||||
public override string PhysicalPath => _source.FileName;
|
||||
|
||||
public override bool Exists => _source.Exists;
|
||||
|
||||
public override Stream Read()
|
||||
{
|
||||
var processedContent = ProcessFileIncludes();
|
||||
return new MemoryStream(Encoding.UTF8.GetBytes(processedContent));
|
||||
}
|
||||
|
||||
private string ProcessFileIncludes()
|
||||
{
|
||||
var basePath = System.IO.Path.GetDirectoryName(_source.PhysicalPath);
|
||||
var cshtmlContent = File.ReadAllText(_source.PhysicalPath);
|
||||
|
||||
var startMatch = "<%$ include: ";
|
||||
var endMatch = " %>";
|
||||
var startIndex = 0;
|
||||
while (startIndex < cshtmlContent.Length)
|
||||
{
|
||||
startIndex = cshtmlContent.IndexOf(startMatch, startIndex);
|
||||
if (startIndex == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
var endIndex = cshtmlContent.IndexOf(endMatch, startIndex);
|
||||
if (endIndex == -1)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid include file format in {_source.PhysicalPath}. Usage example: <%$ include: ErrorPage.js %>");
|
||||
}
|
||||
var includeFileName = cshtmlContent.Substring(startIndex + startMatch.Length, endIndex - (startIndex + startMatch.Length));
|
||||
Console.WriteLine(" Inlining file {0}", includeFileName);
|
||||
var includeFileContent = File.ReadAllText(System.IO.Path.Combine(basePath, includeFileName));
|
||||
cshtmlContent = cshtmlContent.Substring(0, startIndex) + includeFileContent + cshtmlContent.Substring(endIndex + endMatch.Length);
|
||||
startIndex = startIndex + includeFileContent.Length;
|
||||
}
|
||||
return cshtmlContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Builds Razor pages for views in a project. For internal use only.</Description>
|
||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||
<AssemblyName>dotnet-razorpagegenerator</AssemblyName>
|
||||
<PackageId>RazorPageGenerator</PackageId>
|
||||
<OutputType>Exe</OutputType>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
<IsShipping>false</IsShipping>
|
||||
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Razor.Language" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace RazorPageGenerator
|
||||
{
|
||||
public class RazorPageGeneratorResult
|
||||
{
|
||||
public string FilePath { get; set; }
|
||||
|
||||
public string GeneratedCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -103,6 +103,8 @@ namespace MusicStore
|
|||
{
|
||||
options.AddPolicy("ManageStore", new AuthorizationPolicyBuilder().RequireClaim("ManageStore", "Allowed").Build());
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
|
|
@ -123,8 +125,6 @@ namespace MusicStore
|
|||
// During development use the ErrorPage middleware to display error information in the browser
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseDatabaseErrorPage();
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ namespace MusicStore
|
|||
options.Scope.Add("wl.signin");
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
|
|
@ -160,8 +161,6 @@ namespace MusicStore
|
|||
// Note: Not recommended for production.
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseDatabaseErrorPage();
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ namespace MusicStore
|
|||
options.ClientId = "000000004012C08A";
|
||||
options.ClientSecret = "GaMQ2hCnqAC6EcDLnXsAeBVIJOLmeutL";
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
//This method is invoked when ASPNETCORE_ENVIRONMENT is 'Development' or is not defined
|
||||
|
|
@ -132,8 +134,6 @@ namespace MusicStore
|
|||
// During development use the ErrorPage middleware to display error information in the browser
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseDatabaseErrorPage();
|
||||
|
||||
Configure(app);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,8 @@ namespace MusicStore
|
|||
authBuilder.RequireClaim("ManageStore", "Allowed");
|
||||
});
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
|
|
@ -114,7 +116,6 @@ namespace MusicStore
|
|||
// request pipeline.
|
||||
// Note: Not recommended for production.
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ namespace MusicStore
|
|||
options.ClientId = "[ClientId]";
|
||||
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
|
|
@ -122,8 +124,6 @@ namespace MusicStore
|
|||
// During development use the ErrorPage middleware to display error information in the browser
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseDatabaseErrorPage();
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ namespace BlazorServerWeb_CSharp
|
|||
#endif
|
||||
#if (IndividualLocalAuth)
|
||||
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
#endif
|
||||
services.AddSingleton<WeatherForecastService>();
|
||||
}
|
||||
|
|
@ -132,9 +133,6 @@ namespace BlazorServerWeb_CSharp
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ namespace ComponentsWebAssembly_CSharp.Server
|
|||
#else
|
||||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
#endif
|
||||
|
||||
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||
|
|
@ -99,9 +101,6 @@ namespace ComponentsWebAssembly_CSharp.Server
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
app.UseWebAssemblyDebugging();
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ namespace Company.WebApplication1
|
|||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
#endif
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
#elif (OrganizationalAuth)
|
||||
|
|
@ -120,9 +121,6 @@ namespace Company.WebApplication1
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ namespace Company.WebApplication1
|
|||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
#endif
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
|
||||
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
#elif (OrganizationalAuth)
|
||||
|
|
@ -120,9 +122,6 @@ namespace Company.WebApplication1
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ namespace Company.WebApplication1
|
|||
#else
|
||||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
#endif
|
||||
|
||||
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||
|
|
@ -70,9 +72,6 @@ namespace Company.WebApplication1
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ namespace Company.WebApplication1
|
|||
#else
|
||||
options.UseSqlite(
|
||||
Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
#endif
|
||||
|
||||
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||
|
|
@ -72,9 +74,6 @@ namespace Company.WebApplication1
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
#if (IndividualLocalAuth)
|
||||
app.UseDatabaseErrorPage();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ namespace Identity.ExternalClaims
|
|||
// Register no-op EmailSender used by account confirmation and password reset during development
|
||||
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=532713
|
||||
services.AddSingleton<IEmailSender, EmailSender>();
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
|
@ -72,7 +74,6 @@ namespace Identity.ExternalClaims
|
|||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseDatabaseErrorPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue