Database Error Page: Prep for removal of ILogger-based interception.
- Removed EF ApiCheck tests; not required for middleware. - Removed JB annotations and Check usage; align with other Diagnostics Pages projects. - Fixed code redundancies.
This commit is contained in:
parent
bd921bf96d
commit
1b6993fb91
|
|
@ -1,107 +0,0 @@
|
|||
// 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;
|
||||
|
||||
namespace JetBrains.Annotations
|
||||
{
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Method | AttributeTargets.Parameter |
|
||||
AttributeTargets.Property | AttributeTargets.Delegate |
|
||||
AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
|
||||
internal sealed class NotNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Method | AttributeTargets.Parameter |
|
||||
AttributeTargets.Property | AttributeTargets.Delegate |
|
||||
AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
|
||||
internal sealed class CanBeNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
internal sealed class InvokerParameterNameAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
internal sealed class NoEnumerationAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
internal sealed class ContractAnnotationAttribute : Attribute
|
||||
{
|
||||
public string Contract { get; private set; }
|
||||
|
||||
public bool ForceFullStates { get; private set; }
|
||||
|
||||
public ContractAnnotationAttribute([NotNull] string contract)
|
||||
: this(contract, false)
|
||||
{
|
||||
}
|
||||
|
||||
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
|
||||
{
|
||||
Contract = contract;
|
||||
ForceFullStates = forceFullStates;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
|
||||
internal sealed class UsedImplicitlyAttribute : Attribute
|
||||
{
|
||||
public UsedImplicitlyAttribute()
|
||||
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
|
||||
: this(useKindFlags, ImplicitUseTargetFlags.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
|
||||
: this(ImplicitUseKindFlags.Default, targetFlags)
|
||||
{
|
||||
}
|
||||
|
||||
public UsedImplicitlyAttribute(
|
||||
ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
|
||||
{
|
||||
UseKindFlags = useKindFlags;
|
||||
TargetFlags = targetFlags;
|
||||
}
|
||||
|
||||
public ImplicitUseKindFlags UseKindFlags { get; private set; }
|
||||
public ImplicitUseTargetFlags TargetFlags { get; private set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ImplicitUseKindFlags
|
||||
{
|
||||
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
|
||||
Access = 1,
|
||||
Assign = 2,
|
||||
InstantiatedWithFixedConstructorSignature = 4,
|
||||
InstantiatedNoFixedConstructorSignature = 8,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ImplicitUseTargetFlags
|
||||
{
|
||||
Default = Itself,
|
||||
Itself = 1,
|
||||
Members = 2,
|
||||
WithMembers = Itself | Members
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore.Relational.Utilities
|
||||
{
|
||||
internal sealed class ValidatedNotNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Utilities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
|
@ -37,7 +36,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
_log.Value = new DataStoreErrorLog();
|
||||
}
|
||||
|
||||
public virtual void Log<TState>(LogLevel logLevel, EventId eventId, [CanBeNull] TState state, [CanBeNull] Exception exception, [CanBeNull] Func<TState, Exception, string> formatter)
|
||||
public virtual void Log<TState>(
|
||||
LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (exception != null
|
||||
&& LastError != null
|
||||
|
|
@ -73,11 +73,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
private Type _contextType;
|
||||
private Exception _exception;
|
||||
|
||||
public virtual void SetError([NotNull] Type contextType, [NotNull] Exception exception)
|
||||
public virtual void SetError(Type contextType, Exception exception)
|
||||
{
|
||||
Check.NotNull(contextType, nameof(contextType));
|
||||
Check.NotNull(exception, nameof(exception));
|
||||
|
||||
_contextType = contextType;
|
||||
_exception = exception;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -35,12 +36,14 @@ 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>
|
||||
public static IApplicationBuilder UseDatabaseErrorPage(this IApplicationBuilder app, DatabaseErrorPageOptions options)
|
||||
public static IApplicationBuilder UseDatabaseErrorPage(
|
||||
this IApplicationBuilder app, DatabaseErrorPageOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
|
@ -56,4 +59,4 @@ namespace Microsoft.AspNetCore.Builder
|
|||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,8 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Utilities;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
|
@ -26,7 +24,6 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly DatabaseErrorPageOptions _options;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger _logger;
|
||||
private readonly DataStoreErrorLoggerProvider _loggerProvider;
|
||||
|
||||
|
|
@ -34,21 +31,32 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
/// Initializes a new instance of the <see cref="DatabaseErrorPageMiddleware"/> class
|
||||
/// </summary>
|
||||
/// <param name="next">Delegate to execute the next piece of middleware in the request pipeline.</param>
|
||||
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to resolve services from.</param>
|
||||
/// <param name="loggerFactory">
|
||||
/// The <see cref="ILoggerFactory"/> for the application. This middleware both produces logging messages and
|
||||
/// consumes them to detect database related exception.
|
||||
/// </param>
|
||||
/// <param name="options">The options to control what information is displayed on the error page.</param>
|
||||
public DatabaseErrorPageMiddleware([NotNull] RequestDelegate next, [NotNull] IServiceProvider serviceProvider, [NotNull] ILoggerFactory loggerFactory, [NotNull] IOptions<DatabaseErrorPageOptions> options)
|
||||
public DatabaseErrorPageMiddleware(
|
||||
RequestDelegate next,
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<DatabaseErrorPageOptions> options)
|
||||
{
|
||||
Check.NotNull(next, nameof(next));
|
||||
Check.NotNull(serviceProvider, nameof(serviceProvider));
|
||||
Check.NotNull(loggerFactory, nameof(loggerFactory));
|
||||
Check.NotNull(options, nameof(options));
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (loggerFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_serviceProvider = serviceProvider;
|
||||
_options = options.Value;
|
||||
_logger = loggerFactory.CreateLogger<DatabaseErrorPageMiddleware>();
|
||||
|
||||
|
|
@ -63,9 +71,12 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
/// </summary>
|
||||
/// <param name="context">The context for the current request.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public virtual async Task Invoke([NotNull] HttpContext context)
|
||||
public virtual async Task Invoke(HttpContext context)
|
||||
{
|
||||
Check.NotNull(context, "context");
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -80,14 +91,17 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
if (ShouldDisplayErrorPage(_loggerProvider.Logger.LastError, ex, _logger))
|
||||
{
|
||||
var dbContextType = _loggerProvider.Logger.LastError.ContextType;
|
||||
var dbContext = (DbContext)context.RequestServices.GetService(dbContextType);
|
||||
var dbContext = (DbContext) context.RequestServices.GetService(dbContextType);
|
||||
|
||||
if (dbContext == null)
|
||||
{
|
||||
_logger.LogError(Strings.FormatDatabaseErrorPageMiddleware_ContextNotRegistered(dbContextType.FullName));
|
||||
_logger.LogError(
|
||||
Strings.FormatDatabaseErrorPageMiddleware_ContextNotRegistered(dbContextType.FullName));
|
||||
}
|
||||
else
|
||||
{
|
||||
var creator = dbContext.GetService<IDatabaseCreator>() as IRelationalDatabaseCreator;
|
||||
|
||||
if (creator == null)
|
||||
{
|
||||
_logger.LogDebug(Strings.DatabaseErrorPage_NotRelationalDatabase);
|
||||
|
|
@ -101,25 +115,36 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
var modelDiffer = dbContext.GetService<IMigrationsModelDiffer>();
|
||||
|
||||
var appliedMigrations = historyRepository.GetAppliedMigrations();
|
||||
var pendingMigrations = (
|
||||
from m in migrationsAssembly.Migrations
|
||||
|
||||
var pendingMigrations
|
||||
= (from m in migrationsAssembly.Migrations
|
||||
where !appliedMigrations.Any(
|
||||
r => string.Equals(r.MigrationId, m.Key, StringComparison.OrdinalIgnoreCase))
|
||||
r => string.Equals(r.MigrationId, m.Key,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
select m.Key)
|
||||
.ToList();
|
||||
|
||||
// HasDifferences will return true if there is no model snapshot, but if there is an existing database
|
||||
// and no model snapshot then we don't want to show the error page since they are most likely targeting
|
||||
// and existing database and have just misconfigured their model
|
||||
var pendingModelChanges = migrationsAssembly.ModelSnapshot == null && databaseExists
|
||||
? false
|
||||
: modelDiffer.HasDifferences(migrationsAssembly.ModelSnapshot?.Model, dbContext.Model);
|
||||
var pendingModelChanges
|
||||
= (migrationsAssembly.ModelSnapshot != null || !databaseExists)
|
||||
&& modelDiffer.HasDifferences(migrationsAssembly.ModelSnapshot?.Model,
|
||||
dbContext.Model);
|
||||
|
||||
if ((!databaseExists && pendingMigrations.Any()) || pendingMigrations.Any() || pendingModelChanges)
|
||||
if (!databaseExists && pendingMigrations.Any()
|
||||
|| pendingMigrations.Any()
|
||||
|| pendingModelChanges)
|
||||
{
|
||||
var page = new DatabaseErrorPage();
|
||||
page.Model = new DatabaseErrorPageModel(dbContextType, ex, databaseExists, pendingModelChanges, pendingMigrations, _options);
|
||||
var page = new DatabaseErrorPage
|
||||
{
|
||||
Model = new DatabaseErrorPageModel(
|
||||
dbContextType, ex, databaseExists, pendingModelChanges, pendingMigrations,
|
||||
_options)
|
||||
};
|
||||
|
||||
await page.ExecuteAsync(context);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -135,7 +160,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
}
|
||||
}
|
||||
|
||||
private static bool ShouldDisplayErrorPage(DataStoreErrorLogger.DataStoreErrorLog lastError, Exception exception, ILogger logger)
|
||||
private static bool ShouldDisplayErrorPage(DataStoreErrorLogger.DataStoreErrorLog lastError,
|
||||
Exception exception, ILogger logger)
|
||||
{
|
||||
logger.LogDebug(Strings.FormatDatabaseErrorPage_AttemptingToMatchException(exception.GetType()));
|
||||
|
||||
|
|
@ -145,7 +171,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
var match = false;
|
||||
|
||||
for (var e = exception; e != null && !match; e = e.InnerException)
|
||||
{
|
||||
match = lastError.Exception == e;
|
||||
|
|
@ -154,11 +181,13 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
if (!match)
|
||||
{
|
||||
logger.LogDebug(Strings.DatabaseErrorPage_NoMatch);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.LogDebug(Strings.DatabaseErrorPage_Matched);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,7 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Utilities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -20,7 +18,6 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
public class MigrationsEndPointMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger _logger;
|
||||
private readonly MigrationsEndPointOptions _options;
|
||||
|
||||
|
|
@ -28,22 +25,29 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
/// Initializes a new instance of the <see cref="MigrationsEndPointMiddleware"/> class
|
||||
/// </summary>
|
||||
/// <param name="next">Delegate to execute the next piece of middleware in the request pipeline.</param>
|
||||
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to resolve services from.</param>
|
||||
/// <param name="logger">The <see cref="Logger{T}"/> to write messages to.</param>
|
||||
/// <param name="options">The options to control the behavior of the middleware.</param>
|
||||
public MigrationsEndPointMiddleware(
|
||||
[NotNull] RequestDelegate next,
|
||||
[NotNull] IServiceProvider serviceProvider,
|
||||
[NotNull] ILogger<MigrationsEndPointMiddleware> logger,
|
||||
[NotNull] IOptions<MigrationsEndPointOptions> options)
|
||||
RequestDelegate next,
|
||||
ILogger<MigrationsEndPointMiddleware> logger,
|
||||
IOptions<MigrationsEndPointOptions> options)
|
||||
{
|
||||
Check.NotNull(next, "next");
|
||||
Check.NotNull(serviceProvider, "serviceProvider");
|
||||
Check.NotNull(logger, "logger");
|
||||
Check.NotNull(options, "options");
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
|
@ -53,15 +57,19 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
/// </summary>
|
||||
/// <param name="context">The context for the current request.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public virtual async Task Invoke([NotNull] HttpContext context)
|
||||
public virtual async Task Invoke(HttpContext context)
|
||||
{
|
||||
Check.NotNull(context, "context");
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (context.Request.Path.Equals(_options.Path))
|
||||
{
|
||||
_logger.LogDebug(Strings.FormatMigrationsEndPointMiddleware_RequestPathMatched(context.Request.Path));
|
||||
|
||||
var db = await GetDbContext(context, _logger);
|
||||
|
||||
if (db != null)
|
||||
{
|
||||
try
|
||||
|
|
@ -78,8 +86,10 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_Exception(db.GetType().FullName) + ex.ToString();
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_Exception(db.GetType().FullName) + ex;
|
||||
|
||||
_logger.LogError(message);
|
||||
|
||||
throw new InvalidOperationException(message, ex);
|
||||
}
|
||||
}
|
||||
|
|
@ -94,28 +104,39 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
{
|
||||
var form = await context.Request.ReadFormAsync();
|
||||
var contextTypeName = form["context"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(contextTypeName))
|
||||
{
|
||||
logger.LogError(Strings.MigrationsEndPointMiddleware_NoContextType);
|
||||
|
||||
await WriteErrorToResponse(context.Response, Strings.MigrationsEndPointMiddleware_NoContextType);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var contextType = Type.GetType(contextTypeName);
|
||||
|
||||
if (contextType == null)
|
||||
{
|
||||
var message = Strings.FormatMigrationsEndPointMiddleware_InvalidContextType(contextTypeName);
|
||||
|
||||
logger.LogError(message);
|
||||
|
||||
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.LogError(message);
|
||||
|
||||
await WriteErrorToResponse(context.Response, message);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,38 +10,6 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Strings", typeof(Strings).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The string argument '{argumentName}' cannot be empty.
|
||||
/// </summary>
|
||||
internal static string ArgumentIsEmpty
|
||||
{
|
||||
get { return GetString("ArgumentIsEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The string argument '{argumentName}' cannot be empty.
|
||||
/// </summary>
|
||||
internal static string FormatArgumentIsEmpty(object argumentName)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ArgumentIsEmpty", "argumentName"), argumentName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection argument '{argumentName}' must contain at least one element.
|
||||
/// </summary>
|
||||
internal static string CollectionArgumentIsEmpty
|
||||
{
|
||||
get { return GetString("CollectionArgumentIsEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection argument '{argumentName}' must contain at least one element.
|
||||
/// </summary>
|
||||
internal static string FormatCollectionArgumentIsEmpty(object argumentName)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("CollectionArgumentIsEmpty", "argumentName"), argumentName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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. Skipping display of the database error page.
|
||||
/// </summary>
|
||||
|
|
@ -298,22 +266,6 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
return GetString("DatabaseErrorPage_ApplyMigrationsCommandCLI");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value provided for argument '{argumentName}' must be a valid value of enum type '{enumType}'.
|
||||
/// </summary>
|
||||
internal static string InvalidEnumValue
|
||||
{
|
||||
get { return GetString("InvalidEnumValue"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value provided for argument '{argumentName}' must be a valid value of enum type '{enumType}'.
|
||||
/// </summary>
|
||||
internal static string FormatInvalidEnumValue(object argumentName, object enumType)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidEnumValue", "argumentName", "enumType"), argumentName, enumType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Migrations successfully applied for context '{0}'.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -117,12 +117,6 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ArgumentIsEmpty" xml:space="preserve">
|
||||
<value>The string argument '{argumentName}' cannot be empty.</value>
|
||||
</data>
|
||||
<data name="CollectionArgumentIsEmpty" xml:space="preserve">
|
||||
<value>The collection argument '{argumentName}' must contain at least one element.</value>
|
||||
</data>
|
||||
<data name="DatabaseErrorPageMiddleware_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. Skipping display of the database error page.</value>
|
||||
</data>
|
||||
|
|
@ -171,9 +165,6 @@
|
|||
<data name="DatabaseErrorPage_ApplyMigrationsCommandCLI" xml:space="preserve">
|
||||
<value>> dotnet ef database update</value>
|
||||
</data>
|
||||
<data name="InvalidEnumValue" xml:space="preserve">
|
||||
<value>The value provided for argument '{argumentName}' must be a valid value of enum type '{enumType}'.</value>
|
||||
</data>
|
||||
<data name="MigrationsEndPointMiddleware_Applied" xml:space="preserve">
|
||||
<value>Migrations successfully applied for context '{0}'.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
// 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.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Utilities
|
||||
{
|
||||
[DebuggerStepThrough]
|
||||
internal static class Check
|
||||
{
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
if (ReferenceEquals(value, null))
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static IReadOnlyList<T> NotEmpty<T>(IReadOnlyList<T> value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
NotNull(value, parameterName);
|
||||
|
||||
if (value.Count == 0)
|
||||
{
|
||||
throw new ArgumentException(Strings.FormatCollectionArgumentIsEmpty(parameterName));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
[ContractAnnotation("value:null => halt")]
|
||||
public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
{
|
||||
if (ReferenceEquals(parameterName, null))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameterName));
|
||||
}
|
||||
|
||||
if (parameterName.Length == 0)
|
||||
{
|
||||
throw new ArgumentException(Strings.FormatArgumentIsEmpty("parameterName"));
|
||||
}
|
||||
|
||||
if (ReferenceEquals(value, null))
|
||||
{
|
||||
throw new ArgumentNullException(parameterName);
|
||||
}
|
||||
|
||||
if (value.Length == 0)
|
||||
{
|
||||
throw new ArgumentException(Strings.FormatArgumentIsEmpty(parameterName));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static T IsDefined<T>(T value, [InvokerParameterName] [NotNull] string parameterName)
|
||||
where T : struct
|
||||
{
|
||||
NotEmpty(parameterName, nameof(parameterName));
|
||||
|
||||
if (!Enum.IsDefined(typeof(T), value))
|
||||
{
|
||||
throw new ArgumentException(Strings.FormatInvalidEnumValue(parameterName, typeof(T)));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
|
||||
{
|
||||
#line 1 "DatabaseErrorPage.cshtml"
|
||||
using System
|
||||
|
|
@ -19,7 +19,7 @@ using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
|||
#line hidden
|
||||
;
|
||||
#line 4 "DatabaseErrorPage.cshtml"
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
@using System
|
||||
@using System.Linq
|
||||
@using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
|
||||
@using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews
|
||||
@using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
|
||||
@{
|
||||
Response.StatusCode = 500;
|
||||
Response.ContentType = "text/html; charset=utf-8";
|
||||
|
|
|
|||
|
|
@ -1,72 +1,35 @@
|
|||
// 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 JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
|
||||
{
|
||||
internal class DatabaseErrorPageModel
|
||||
{
|
||||
private readonly Type _contextType;
|
||||
private readonly Exception _exception;
|
||||
private readonly bool _databaseExists;
|
||||
private readonly bool _pendingModelChanges;
|
||||
private readonly IEnumerable<string> _pendingMigrations;
|
||||
private readonly DatabaseErrorPageOptions _options;
|
||||
|
||||
public DatabaseErrorPageModel(
|
||||
[NotNull] Type contextType,
|
||||
[NotNull] Exception exception,
|
||||
Type contextType,
|
||||
Exception exception,
|
||||
bool databaseExists,
|
||||
bool pendingModelChanges,
|
||||
[NotNull] IEnumerable<string> pendingMigrations,
|
||||
[NotNull] DatabaseErrorPageOptions options)
|
||||
IEnumerable<string> pendingMigrations,
|
||||
DatabaseErrorPageOptions options)
|
||||
{
|
||||
Check.NotNull(contextType, "contextType");
|
||||
Check.NotNull(exception, "exception");
|
||||
Check.NotNull(pendingMigrations, "pendingMigrations");
|
||||
Check.NotNull(options, "options");
|
||||
|
||||
_contextType = contextType;
|
||||
_exception = exception;
|
||||
_databaseExists = databaseExists;
|
||||
_pendingModelChanges = pendingModelChanges;
|
||||
_pendingMigrations = pendingMigrations;
|
||||
_options = options;
|
||||
ContextType = contextType;
|
||||
Exception = exception;
|
||||
DatabaseExists = databaseExists;
|
||||
PendingModelChanges = pendingModelChanges;
|
||||
PendingMigrations = pendingMigrations;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public virtual Type ContextType
|
||||
{
|
||||
get { return _contextType; }
|
||||
}
|
||||
|
||||
public virtual Exception Exception
|
||||
{
|
||||
get { return _exception; }
|
||||
}
|
||||
|
||||
public virtual bool DatabaseExists
|
||||
{
|
||||
get { return _databaseExists; }
|
||||
}
|
||||
|
||||
public virtual bool PendingModelChanges
|
||||
{
|
||||
get { return _pendingModelChanges; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> PendingMigrations
|
||||
{
|
||||
get { return _pendingMigrations; }
|
||||
}
|
||||
|
||||
public virtual DatabaseErrorPageOptions Options
|
||||
{
|
||||
get { return _options; }
|
||||
}
|
||||
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 DatabaseErrorPageOptions Options { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,5 +6,15 @@
|
|||
{
|
||||
"TypeId": "public class Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views.DatabaseErrorPageModel",
|
||||
"Kind": "Removal"
|
||||
},
|
||||
{
|
||||
"TypeId": "public class Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware",
|
||||
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.RequestDelegate next, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware> logger, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.MigrationsEndPointOptions> options)",
|
||||
"Kind": "Removal"
|
||||
},
|
||||
{
|
||||
"TypeId": "public class Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware",
|
||||
"MemberId": "public .ctor(Microsoft.AspNetCore.Http.RequestDelegate next, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.DatabaseErrorPageOptions> options)",
|
||||
"Kind": "Removal"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// 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 Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoreTests
|
||||
{
|
||||
public class ApiConsistencyTest : ApiConsistencyTestBase
|
||||
{
|
||||
protected override Assembly TargetAssembly => typeof(DatabaseErrorPageMiddleware).GetTypeInfo().Assembly;
|
||||
|
||||
protected override IEnumerable<string> GetCancellationTokenExceptions()
|
||||
{
|
||||
return new string[]
|
||||
{
|
||||
"DatabaseErrorPageMiddleware.Invoke",
|
||||
"MigrationsEndPointMiddleware.Invoke",
|
||||
"DatabaseErrorPage.ExecuteAsync",
|
||||
"BaseView.ExecuteAsync"
|
||||
};
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetAsyncSuffixExceptions()
|
||||
{
|
||||
return new string[]
|
||||
{
|
||||
"DatabaseErrorPageMiddleware.Invoke",
|
||||
"MigrationsEndPointMiddleware.Invoke"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public abstract class ApiConsistencyTestBase
|
||||
{
|
||||
private static readonly HashSet<TypeInfo> _typesToSkip = new HashSet<TypeInfo>
|
||||
{
|
||||
typeof(DatabaseErrorPage).GetTypeInfo()
|
||||
};
|
||||
|
||||
protected const BindingFlags PublicInstance
|
||||
= BindingFlags.Instance | BindingFlags.Public;
|
||||
|
||||
protected const BindingFlags AnyInstance
|
||||
= BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
|
||||
[ConditionalFact(Skip = "Skipping until we update to mono")]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
public void Public_inheritable_apis_should_be_virtual()
|
||||
{
|
||||
var nonVirtualMethods
|
||||
= (from type in GetAllTypes(TargetAssembly.DefinedTypes)
|
||||
where type.IsVisible
|
||||
&& !type.IsSealed
|
||||
&& type.DeclaredConstructors.Any(c => c.IsPublic || c.IsFamily || c.IsFamilyOrAssembly)
|
||||
&& type.Namespace != null
|
||||
&& !type.Namespace.EndsWith(".Compiled")
|
||||
&& !_typesToSkip.Contains(type)
|
||||
from method in type.DeclaredMethods.Where(m => m.IsPublic && !m.IsStatic)
|
||||
where method.DeclaringType.GetTypeInfo() == type
|
||||
&& !(method.IsVirtual && !method.IsFinal)
|
||||
select type.FullName + "." + method.Name)
|
||||
.ToList();
|
||||
|
||||
Assert.False(
|
||||
nonVirtualMethods.Any(),
|
||||
"\r\n-- Missing virtual APIs --\r\n" + string.Join("\r\n", nonVirtualMethods));
|
||||
}
|
||||
|
||||
[ConditionalFact(Skip = "Skipping until we update to mono")]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
public void Public_api_arguments_should_have_not_null_annotation()
|
||||
{
|
||||
var parametersMissingAttribute
|
||||
= (from type in GetAllTypes(TargetAssembly.DefinedTypes)
|
||||
where type.IsVisible && !typeof(Delegate).GetTypeInfo().IsAssignableFrom(type) && !_typesToSkip.Contains(type)
|
||||
let interfaceMappings = type.ImplementedInterfaces.Select(type.GetRuntimeInterfaceMap)
|
||||
let events = type.DeclaredEvents
|
||||
from method in type.DeclaredMethods.Where(m => m.IsPublic)
|
||||
.Concat<MethodBase>(type.DeclaredConstructors)
|
||||
where method.DeclaringType.GetTypeInfo() == type
|
||||
where type.IsInterface || !interfaceMappings.Any(im => im.TargetMethods.Contains(method))
|
||||
where !events.Any(e => e.AddMethod == method || e.RemoveMethod == method)
|
||||
from parameter in method.GetParameters()
|
||||
where !parameter.ParameterType.GetTypeInfo().IsValueType
|
||||
&& !parameter.GetCustomAttributes()
|
||||
.Any(
|
||||
a => a.GetType().Name == "NotNullAttribute"
|
||||
|| a.GetType().Name == "CanBeNullAttribute")
|
||||
select type.FullName + "." + method.Name + "[" + parameter.Name + "]")
|
||||
.ToList();
|
||||
|
||||
Assert.False(
|
||||
parametersMissingAttribute.Any(),
|
||||
"\r\n-- Missing NotNull annotations --\r\n" + string.Join("\r\n", parametersMissingAttribute));
|
||||
}
|
||||
|
||||
[ConditionalFact(Skip = "Skipping until we update to mono")]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
public void Async_methods_should_have_overload_with_cancellation_token_and_end_with_async_suffix()
|
||||
{
|
||||
var asyncMethods
|
||||
= (from type in GetAllTypes(TargetAssembly.DefinedTypes)
|
||||
where type.IsVisible
|
||||
from method in type.DeclaredMethods.Where(m => m.IsPublic)
|
||||
where method.DeclaringType.GetTypeInfo() == type
|
||||
where typeof(Task).IsAssignableFrom(method.ReturnType)
|
||||
select method).ToList();
|
||||
|
||||
var asyncMethodsWithToken
|
||||
= (from method in asyncMethods
|
||||
where method.GetParameters().Any(pi => pi.ParameterType == typeof(CancellationToken))
|
||||
select method).ToList();
|
||||
|
||||
var asyncMethodsWithoutToken
|
||||
= (from method in asyncMethods
|
||||
where method.GetParameters().All(pi => pi.ParameterType != typeof(CancellationToken))
|
||||
select method).ToList();
|
||||
|
||||
var missingOverloads
|
||||
= (from methodWithoutToken in asyncMethodsWithoutToken
|
||||
where !asyncMethodsWithToken
|
||||
.Any(methodWithToken => methodWithoutToken.Name == methodWithToken.Name
|
||||
&& methodWithoutToken.DeclaringType == methodWithToken.DeclaringType)
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
select methodWithoutToken.DeclaringType.Name + "." + methodWithoutToken.Name)
|
||||
.Except(GetCancellationTokenExceptions())
|
||||
.ToList();
|
||||
|
||||
Assert.False(
|
||||
missingOverloads.Any(),
|
||||
"\r\n-- Missing async overloads --\r\n" + string.Join("\r\n", missingOverloads));
|
||||
|
||||
var missingSuffixMethods
|
||||
= asyncMethods
|
||||
.Where(method => !method.Name.EndsWith("Async"))
|
||||
.Select(method => method.DeclaringType.Name + "." + method.Name)
|
||||
.Except(GetAsyncSuffixExceptions())
|
||||
.ToList();
|
||||
|
||||
Assert.False(
|
||||
missingSuffixMethods.Any(),
|
||||
"\r\n-- Missing async suffix --\r\n" + string.Join("\r\n", missingSuffixMethods));
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<string> GetCancellationTokenExceptions()
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<string> GetAsyncSuffixExceptions()
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
protected abstract Assembly TargetAssembly { get; }
|
||||
|
||||
protected virtual IEnumerable<TypeInfo> GetAllTypes(IEnumerable<TypeInfo> typeInfos)
|
||||
{
|
||||
foreach (var typeInfo in typeInfos)
|
||||
{
|
||||
yield return typeInfo;
|
||||
|
||||
foreach (var nestedType in GetAllTypes(typeInfo.DeclaredNestedTypes))
|
||||
{
|
||||
yield return nestedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.RazorViews;
|
||||
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
exception: new Exception(),
|
||||
databaseExists: false,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" },
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
|
@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" },
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: true,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" },
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
|
@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
|
|||
exception: new Exception(),
|
||||
databaseExists: true,
|
||||
pendingModelChanges: false,
|
||||
pendingMigrations: new string[] { "111_MigrationOne" },
|
||||
pendingMigrations: new[] { "111_MigrationOne" },
|
||||
options: options);
|
||||
|
||||
var content = await ExecutePage(options, model);
|
||||
|
|
|
|||
Loading…
Reference in New Issue