Copy of Microsoft.AspNet.Diagnostics.Entity code from EntityFramework

Straight copy of code with no modification so that we have a baseline of changes made to port the code to the Diagnostics repo
This commit is contained in:
rowanmiller 2014-11-17 15:49:09 -08:00
parent b466013235
commit fad5ba4323
34 changed files with 3204 additions and 0 deletions

View File

@ -0,0 +1,108 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using Microsoft.Data.Entity.Storage;
using Microsoft.Framework.Logging;
using System;
#if ASPNETCORE50
using System.Threading;
#else
using System.Runtime.Remoting.Messaging;
#endif
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class DataStoreErrorLogger : ILogger
{
#if ASPNETCORE50
private readonly AsyncLocal<DataStoreErrorLog> _log = new AsyncLocal<DataStoreErrorLog>();
#else
private const string ContextName = "__DataStoreErrorLog";
#endif
public virtual DataStoreErrorLog LastError
{
get
{
#if ASPNETCORE50
return _log.Value;
#else
return (DataStoreErrorLog)CallContext.LogicalGetData(ContextName);
#endif
}
}
public virtual void StartLoggingForCurrentCallContext()
{
// Because CallContext is cloned at each async operation we cannot
// lazily create the error object when an error is encountered, otherwise
// it will not be available to code outside of the current async context.
// We create it ahead of time so that any cloning just clones the reference
// to the object that will hold any errors.
#if ASPNETCORE50
_log.Value = new DataStoreErrorLog();
#else
CallContext.LogicalSetData(ContextName, new DataStoreErrorLog());
#endif
}
public virtual void Write(LogLevel logLevel, int eventId, [CanBeNull] object state, [CanBeNull] Exception exception, [CanBeNull] Func<object, Exception, string> formatter)
{
var errorState = state as DataStoreErrorLogState;
if (errorState != null && exception != null && LastError != null)
{
LastError.SetError(errorState.ContextType, exception);
}
}
public virtual bool IsEnabled(LogLevel logLevel)
{
return true;
}
public virtual IDisposable BeginScope(object state)
{
return NullScope.Instance;
}
private class NullScope : IDisposable
{
public static NullScope Instance = new NullScope();
public void Dispose()
{ }
}
public class DataStoreErrorLog
{
private Type _contextType;
private Exception _exception;
public virtual void SetError([NotNull] Type contextType, [NotNull] Exception exception)
{
Check.NotNull(contextType, "contextType");
Check.NotNull(exception, "exception");
_contextType = contextType;
_exception = exception;
}
public virtual bool IsErrorLogged
{
get { return _exception != null; }
}
public virtual Type ContextType
{
get { return _contextType; }
}
public virtual Exception Exception
{
get { return _exception; }
}
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class DataStoreErrorLoggerProvider : ILoggerProvider
{
private readonly DataStoreErrorLogger _logger = new DataStoreErrorLogger();
public virtual ILogger Create(string name)
{
return _logger;
}
public virtual DataStoreErrorLogger Logger
{
get { return _logger; }
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
using Microsoft.AspNet.Diagnostics.Entity;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
namespace Microsoft.AspNet.Builder
{
public static class DatabaseErrorPageExtensions
{
public static IApplicationBuilder UseDatabaseErrorPage([NotNull] this IApplicationBuilder builder)
{
Check.NotNull(builder, "builder");
return builder.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
}
public static IApplicationBuilder UseDatabaseErrorPage([NotNull] this IApplicationBuilder builder, [NotNull] DatabaseErrorPageOptions options)
{
Check.NotNull(builder, "builder");
Check.NotNull(options, "options");
builder = builder.UseMiddleware<DatabaseErrorPageMiddleware>(options);
if(options.EnableMigrationCommands)
{
builder.UseMigrationsEndPoint(new MigrationsEndPointOptions { Path = options.MigrationsEndPointPath });
}
return builder;
}
}
}

View File

@ -0,0 +1,116 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using Microsoft.AspNet.Diagnostics.Entity.Views;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.RequestContainer;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Migrations;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using Microsoft.Data.Entity.Migrations.Utilities;
using Microsoft.Data.Entity.Relational;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class DatabaseErrorPageMiddleware
{
private readonly RequestDelegate _next;
private readonly DatabaseErrorPageOptions _options;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly DataStoreErrorLoggerProvider _loggerProvider;
public DatabaseErrorPageMiddleware([NotNull] RequestDelegate next, [NotNull] IServiceProvider serviceProvider, [NotNull] ILoggerFactory loggerFactory, [NotNull] DatabaseErrorPageOptions options)
{
Check.NotNull(next, "next");
Check.NotNull(serviceProvider, "serviceProvider");
Check.NotNull(loggerFactory, "loggerFactory");
Check.NotNull(options, "options");
_next = next;
_serviceProvider = serviceProvider;
_options = options;
_logger = loggerFactory.Create<DatabaseErrorPageMiddleware>();
_loggerProvider = new DataStoreErrorLoggerProvider();
loggerFactory.AddProvider(_loggerProvider);
}
public virtual async Task Invoke([NotNull] HttpContext context)
{
Check.NotNull(context, "context");
try
{
#if !ASPNETCORE50
// TODO This probably isn't the correct place for this workaround, it
// needs to be called before anything is written to CallContext
// http://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx
System.Configuration.ConfigurationManager.GetSection("system.xml/xmlReader");
#endif
_loggerProvider.Logger.StartLoggingForCurrentCallContext();
await _next(context).WithCurrentCulture();
}
catch (Exception ex)
{
try
{
if (_loggerProvider.Logger.LastError.IsErrorLogged
&& _loggerProvider.Logger.LastError.Exception == ex)
{
using (RequestServicesContainer.EnsureRequestServices(context, _serviceProvider))
{
var dbContextType = _loggerProvider.Logger.LastError.ContextType;
var dbContext = (DbContext)context.RequestServices.GetService(dbContextType);
if (dbContext == null)
{
_logger.WriteError(Strings.DatabaseErrorPageMiddleware_ContextNotRegistered(dbContextType.FullName));
}
else
{
if (dbContext.Database is RelationalDatabase)
{
var databaseExists = dbContext.Database.AsRelational().Exists();
var services = (MigrationsDataStoreServices)dbContext.Configuration.DataStoreServices;
var pendingMigrations = services.Migrator.GetPendingMigrations().Select(m => m.GetMigrationId());
var pendingModelChanges = true;
var snapshot = services.Migrator.MigrationAssembly.Model;
if (snapshot != null)
{
pendingModelChanges = services.Migrator.ModelDiffer.Diff(snapshot, dbContext.Model).Any();
}
if ((!databaseExists && pendingMigrations.Any()) || pendingMigrations.Any() || pendingModelChanges)
{
var page = new DatabaseErrorPage();
page.Model = new DatabaseErrorPageModel(dbContextType, ex, databaseExists, pendingModelChanges, pendingMigrations, _options);
await page.ExecuteAsync(context).WithCurrentCulture();
return;
}
}
}
}
}
}
catch (Exception e)
{
_logger.WriteError(Strings.DatabaseErrorPageMiddleware_Exception, e);
}
throw;
}
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class DatabaseErrorPageOptions
{
private PathString _migrationsEndPointPath = MigrationsEndPointOptions.DefaultPath;
private bool _defaultVisibility;
private bool? _showExceptionDetails;
private bool? _listMigrations;
private bool? _enableMigrationCommands;
public static DatabaseErrorPageOptions ShowAll
{
get
{
// We don't use a static instance because it's mutable.
return new DatabaseErrorPageOptions()
{
ShowExceptionDetails = true,
ListMigrations = true,
EnableMigrationCommands = true
};
}
}
public virtual PathString MigrationsEndPointPath
{
get { return _migrationsEndPointPath; }
set
{
Check.NotNull(value, "value");
_migrationsEndPointPath = value;
}
}
public virtual bool ShowExceptionDetails
{
get { return _showExceptionDetails ?? _defaultVisibility; }
set { _showExceptionDetails = value; }
}
public virtual bool ListMigrations
{
get { return _listMigrations ?? _defaultVisibility; }
set { _listMigrations = value; }
}
public virtual bool EnableMigrationCommands
{
get { return _enableMigrationCommands ?? _defaultVisibility; }
set { _enableMigrationCommands = value; }
}
public virtual void SetDefaultVisibility(bool isVisible)
{
_defaultVisibility = isVisible;
}
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>4f5a6a72-ffe4-49c4-b4c6-58132cfcb9fe</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
using Microsoft.AspNet.Diagnostics.Entity;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
namespace Microsoft.AspNet.Builder
{
public static class MigrationsEndPointExtensions
{
public static IApplicationBuilder UseMigrationsEndPoint([NotNull] this IApplicationBuilder builder)
{
Check.NotNull(builder, "builder");
return builder.UseMigrationsEndPoint(new MigrationsEndPointOptions());
}
public static IApplicationBuilder UseMigrationsEndPoint([NotNull] this IApplicationBuilder builder, [NotNull] MigrationsEndPointOptions options)
{
Check.NotNull(builder, "builder");
Check.NotNull(options, "options");
return builder.UseMiddleware<MigrationsEndPointMiddleware>(options);
}
}
}

View File

@ -0,0 +1,123 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using Microsoft.AspNet.Http;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using Microsoft.Framework.DependencyInjection;
using System.Net;
using Microsoft.Framework.Logging;
using Microsoft.AspNet.RequestContainer;
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class MigrationsEndPointMiddleware
{
private readonly RequestDelegate _next;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly MigrationsEndPointOptions _options;
public MigrationsEndPointMiddleware([NotNull] RequestDelegate next, [NotNull] IServiceProvider serviceProvider, [NotNull] ILoggerFactory loggerFactory, [NotNull] MigrationsEndPointOptions options)
{
Check.NotNull(next, "next");
Check.NotNull(serviceProvider, "serviceProvider");
Check.NotNull(loggerFactory, "loggerFactory");
Check.NotNull(options, "options");
_next = next;
_serviceProvider = serviceProvider;
_logger = loggerFactory.Create<MigrationsEndPointMiddleware>();
_options = options;
}
public virtual async Task Invoke([NotNull] HttpContext context)
{
Check.NotNull(context, "context");
if (context.Request.Path.Equals(_options.Path))
{
_logger.WriteVerbose(Strings.MigrationsEndPointMiddleware_RequestPathMatched(context.Request.Path));
using (RequestServicesContainer.EnsureRequestServices(context, _serviceProvider))
{
var db = await GetDbContext(context, _logger).WithCurrentCulture();
if (db != null)
{
try
{
_logger.WriteVerbose(Strings.MigrationsEndPointMiddleware_ApplyingMigrations(db.GetType().FullName));
db.Database.AsMigrationsEnabled().ApplyMigrations();
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
context.Response.Headers.Add("Pragma", new[] { "no-cache" });
context.Response.Headers.Add("Cache-Control", new[] { "no-cache" });
_logger.WriteVerbose(Strings.MigrationsEndPointMiddleware_Applied(db.GetType().FullName));
}
catch (Exception ex)
{
var message = Strings.MigrationsEndPointMiddleware_Exception(db.GetType().FullName);
_logger.WriteError(message);
throw new InvalidOperationException(message, ex);
}
}
}
}
else
{
await _next(context).WithCurrentCulture();
}
}
private static async Task<DbContext> GetDbContext(HttpContext context, ILogger logger)
{
var form = await context.Request.GetFormAsync().WithCurrentCulture();
var contextTypeName = form["context"];
if (string.IsNullOrWhiteSpace(contextTypeName))
{
logger.WriteError(Strings.MigrationsEndPointMiddleware_NoContextType);
await WriteErrorToResponse(context.Response, Strings.MigrationsEndPointMiddleware_NoContextType).WithCurrentCulture();
return null;
}
var contextType = Type.GetType(contextTypeName);
if (contextType == null)
{
var message = Strings.MigrationsEndPointMiddleware_InvalidContextType(contextTypeName);
logger.WriteError(message);
await WriteErrorToResponse(context.Response, message).WithCurrentCulture();
return null;
}
var db = (DbContext)context.RequestServices.GetService(contextType);
if (db == null)
{
var message = Strings.MigrationsEndPointMiddleware_ContextNotRegistered(contextType.FullName);
logger.WriteError(message);
await WriteErrorToResponse(context.Response, message).WithCurrentCulture();
return null;
}
return db;
}
private static async Task WriteErrorToResponse(HttpResponse response, string error)
{
response.StatusCode = (int)HttpStatusCode.BadRequest;
response.Headers.Add("Pragma", new[] { "no-cache" });
response.Headers.Add("Cache-Control", new[] { "no-cache" });
response.ContentType = "text/plain";
// Padding to >512 to ensure IE doesn't hide the message
// http://stackoverflow.com/questions/16741062/what-rules-does-ie-use-to-determine-whether-to-show-the-entity-body
await response.WriteAsync(error.PadRight(513)).WithCurrentCulture();
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Diagnostics.Entity
{
public class MigrationsEndPointOptions
{
public static PathString DefaultPath = new PathString("/ApplyDatabaseMigrations");
private PathString _path = DefaultPath;
public virtual PathString Path
{
get { return _path; }
set
{
Check.NotNull(value, "value");
_path = value;
}
}
}
}

View File

@ -0,0 +1,256 @@
// <auto-generated />
namespace Microsoft.AspNet.Diagnostics.Entity
{
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Resources;
using JetBrains.Annotations;
public static class Strings
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Diagnostics.Entity.Strings", typeof(Strings).GetTypeInfo().Assembly);
/// <summary>
/// The string argument '{argumentName}' cannot be empty.
/// </summary>
public static string ArgumentIsEmpty([CanBeNull] object argumentName)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ArgumentIsEmpty", "argumentName"), argumentName);
}
/// <summary>
/// The collection argument '{argumentName}' must contain at least one element.
/// </summary>
public static string CollectionArgumentIsEmpty([CanBeNull] 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&lt;{0}&gt;() inside the UseServices(...) call in your application startup code. Skipping display of the database error page.
/// </summary>
public static string DatabaseErrorPageMiddleware_ContextNotRegistered([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPageMiddleware_ContextNotRegistered"), p0);
}
/// <summary>
/// An exception occurred while calculating the database error page content. Skipping display of the database error page.
/// </summary>
public static string DatabaseErrorPageMiddleware_Exception
{
get { return GetString("DatabaseErrorPageMiddleware_Exception"); }
}
/// <summary>
/// &gt; k ef migration add [migration name]
/// </summary>
public static string DatabaseErrorPage_AddMigrationCommand
{
get { return GetString("DatabaseErrorPage_AddMigrationCommand"); }
}
/// <summary>
/// Apply Migrations
/// </summary>
public static string DatabaseErrorPage_ApplyMigrationsButton
{
get { return GetString("DatabaseErrorPage_ApplyMigrationsButton"); }
}
/// <summary>
/// Migrations Applied
/// </summary>
public static string DatabaseErrorPage_ApplyMigrationsButtonDone
{
get { return GetString("DatabaseErrorPage_ApplyMigrationsButtonDone"); }
}
/// <summary>
/// Applying Migrations...
/// </summary>
public static string DatabaseErrorPage_ApplyMigrationsButtonRunning
{
get { return GetString("DatabaseErrorPage_ApplyMigrationsButtonRunning"); }
}
/// <summary>
/// An error occurred applying migrations, try applying them from the command line
/// </summary>
public static string DatabaseErrorPage_ApplyMigrationsFailed
{
get { return GetString("DatabaseErrorPage_ApplyMigrationsFailed"); }
}
/// <summary>
/// You can also apply migrations from the command line:
/// </summary>
public static string DatabaseErrorPage_HowToApplyFromCmd
{
get { return GetString("DatabaseErrorPage_HowToApplyFromCmd"); }
}
/// <summary>
/// Try refreshing the page
/// </summary>
public static string DatabaseErrorPage_MigrationsAppliedRefresh
{
get { return GetString("DatabaseErrorPage_MigrationsAppliedRefresh"); }
}
/// <summary>
/// From the command line, scaffold a new migration and apply it to the database:
/// </summary>
public static string DatabaseErrorPage_NoDbOrMigrationsInfo
{
get { return GetString("DatabaseErrorPage_NoDbOrMigrationsInfo"); }
}
/// <summary>
/// Use migrations to create the database for {0}
/// </summary>
public static string DatabaseErrorPage_NoDbOrMigrationsTitle([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_NoDbOrMigrationsTitle"), p0);
}
/// <summary>
/// Scaffold a new migration for these changes and apply them to the database from the command line:
/// </summary>
public static string DatabaseErrorPage_PendingChangesInfo
{
get { return GetString("DatabaseErrorPage_PendingChangesInfo"); }
}
/// <summary>
/// There are pending model changes for {0}
/// </summary>
public static string DatabaseErrorPage_PendingChangesTitle([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_PendingChangesTitle"), p0);
}
/// <summary>
/// There are migrations for {0} that have not been applied to the database
/// </summary>
public static string DatabaseErrorPage_PendingMigrationsInfo([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_PendingMigrationsInfo"), p0);
}
/// <summary>
/// Applying existing migrations for {0} may resolve this issue
/// </summary>
public static string DatabaseErrorPage_PendingMigrationsTitle([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_PendingMigrationsTitle"), p0);
}
/// <summary>
/// &gt; k ef migration apply
/// </summary>
public static string DatabaseErrorPage_ApplyMigrationsCommand
{
get { return GetString("DatabaseErrorPage_ApplyMigrationsCommand"); }
}
/// <summary>
/// The value provided for argument '{argumentName}' must be a valid value of enum type '{enumType}'.
/// </summary>
public static string InvalidEnumValue([CanBeNull] object argumentName, [CanBeNull] object enumType)
{
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidEnumValue", "argumentName", "enumType"), argumentName, enumType);
}
/// <summary>
/// Migrations successfully applied for context '{0}'.
/// </summary>
public static string MigrationsEndPointMiddleware_Applied([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_Applied"), p0);
}
/// <summary>
/// Request is valid, applying migrations for context '{0}'.
/// </summary>
public static string MigrationsEndPointMiddleware_ApplyingMigrations([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_ApplyingMigrations"), p0);
}
/// <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&lt;{0}&gt;() inside the UseServices(...) call in your application startup code.
/// </summary>
public static string MigrationsEndPointMiddleware_ContextNotRegistered([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_ContextNotRegistered"), p0);
}
/// <summary>
/// An error occurred while applying the migrations for '{0}'. See InnerException for details.
/// </summary>
public static string MigrationsEndPointMiddleware_Exception([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_Exception"), p0);
}
/// <summary>
/// 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.
/// </summary>
public static string MigrationsEndPointMiddleware_InvalidContextType([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_InvalidContextType"), p0);
}
/// <summary>
/// No context type was specified. Ensure the form data from the request includes a contextTypeName value, specifying the context to apply migrations for.
/// </summary>
public static string MigrationsEndPointMiddleware_NoContextType
{
get { return GetString("MigrationsEndPointMiddleware_NoContextType"); }
}
/// <summary>
/// Request path matched the path configured for this migrations endpoint ({0}). Attempting to process the migrations request.
/// </summary>
public static string MigrationsEndPointMiddleware_RequestPathMatched([CanBeNull] object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_RequestPathMatched"), p0);
}
/// <summary>
/// A database operation failed while processing the request.
/// </summary>
public static string DatabaseErrorPage_Title
{
get { return GetString("DatabaseErrorPage_Title"); }
}
/// <summary>
/// To use migrations from a command prompt you will need to &lt;a href='http://go.microsoft.com/fwlink/?LinkId=518242'&gt;install K Version Manager (KVM)&lt;/a&gt;. Once installed, you can run migration commands from a standard command prompt in the project directory.
/// </summary>
public static string DatabaseErrorPage_EnableMigrationsCommandsInfo
{
get { return GetString("DatabaseErrorPage_EnableMigrationsCommandsInfo"); }
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<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&lt;{0}&gt;() inside the UseServices(...) call in your application startup code. Skipping display of the database error page.</value>
</data>
<data name="DatabaseErrorPageMiddleware_Exception" xml:space="preserve">
<value>An exception occurred while calculating the database error page content. Skipping display of the database error page.</value>
</data>
<data name="DatabaseErrorPage_AddMigrationCommand" xml:space="preserve">
<value>&gt; k ef migration add [migration name]</value>
</data>
<data name="DatabaseErrorPage_ApplyMigrationsButton" xml:space="preserve">
<value>Apply Migrations</value>
</data>
<data name="DatabaseErrorPage_ApplyMigrationsButtonDone" xml:space="preserve">
<value>Migrations Applied</value>
</data>
<data name="DatabaseErrorPage_ApplyMigrationsButtonRunning" xml:space="preserve">
<value>Applying Migrations...</value>
</data>
<data name="DatabaseErrorPage_ApplyMigrationsFailed" xml:space="preserve">
<value>An error occurred applying migrations, try applying them from the command line</value>
</data>
<data name="DatabaseErrorPage_HowToApplyFromCmd" xml:space="preserve">
<value>You can also apply migrations from the command line:</value>
</data>
<data name="DatabaseErrorPage_MigrationsAppliedRefresh" xml:space="preserve">
<value>Try refreshing the page</value>
</data>
<data name="DatabaseErrorPage_NoDbOrMigrationsInfo" xml:space="preserve">
<value>From the command line, 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>
</data>
<data name="DatabaseErrorPage_PendingChangesInfo" xml:space="preserve">
<value>Scaffold a new migration for these changes and apply them to the database from the command line:</value>
</data>
<data name="DatabaseErrorPage_PendingChangesTitle" xml:space="preserve">
<value>There are pending model changes for {0}</value>
</data>
<data name="DatabaseErrorPage_PendingMigrationsInfo" xml:space="preserve">
<value>There are migrations for {0} that have not been applied to the database</value>
</data>
<data name="DatabaseErrorPage_PendingMigrationsTitle" xml:space="preserve">
<value>Applying existing migrations for {0} may resolve this issue</value>
</data>
<data name="DatabaseErrorPage_ApplyMigrationsCommand" xml:space="preserve">
<value>&gt; k ef migration apply</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>
<data name="MigrationsEndPointMiddleware_ApplyingMigrations" xml:space="preserve">
<value>Request is valid, applying migrations for context '{0}'.</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&lt;{0}&gt;() inside the UseServices(...) 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 contextTypeName value, specifying the context to apply migrations for.</value>
</data>
<data name="MigrationsEndPointMiddleware_RequestPathMatched" xml:space="preserve">
<value>Request path matched the path configured for this migrations endpoint ({0}). Attempting to process the migrations request.</value>
</data>
<data name="DatabaseErrorPage_Title" xml:space="preserve">
<value>A database operation failed while processing the request.</value>
</data>
<data name="DatabaseErrorPage_EnableMigrationsCommandsInfo" xml:space="preserve">
<value>To use migrations from a command prompt you will need to &lt;a href='http://go.microsoft.com/fwlink/?LinkId=518242'&gt;install K Version Manager (KVM)&lt;/a&gt;. Once installed, you can run migration commands from a standard command prompt in the project directory.</value>
</data>
</root>

View File

@ -0,0 +1,80 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;
namespace Microsoft.AspNet.Diagnostics.Entity.Utilities
{
[DebuggerStepThrough]
internal static class Check
{
[ContractAnnotation("value:null => halt")]
public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName)
{
NotEmpty(parameterName, "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, "parameterName");
NotNull(value, parameterName);
if (value.Count == 0)
{
throw new ArgumentException(Strings.CollectionArgumentIsEmpty(parameterName));
}
return value;
}
[ContractAnnotation("value:null => halt")]
public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
{
if (ReferenceEquals(parameterName, null))
{
throw new ArgumentNullException("parameterName");
}
if (parameterName.Length == 0)
{
throw new ArgumentException(Strings.ArgumentIsEmpty("parameterName"));
}
if (ReferenceEquals(value, null))
{
throw new ArgumentNullException(parameterName);
}
if (value.Length == 0)
{
throw new ArgumentException(Strings.ArgumentIsEmpty(parameterName));
}
return value;
}
public static T IsDefined<T>(T value, [InvokerParameterName] [NotNull] string parameterName)
where T : struct
{
NotEmpty(parameterName, "parameterName");
if (!Enum.IsDefined(typeof(T), value))
{
throw new ArgumentException(Strings.InvalidEnumValue(parameterName, typeof(T)));
}
return value;
}
}
}

View File

@ -0,0 +1,429 @@
namespace Microsoft.AspNet.Diagnostics.Entity.Views
{
#line 1 "DatabaseErrorPage.cshtml"
using System
#line default
#line hidden
;
#line 2 "DatabaseErrorPage.cshtml"
using System.Linq
#line default
#line hidden
;
#line 3 "DatabaseErrorPage.cshtml"
using JetBrains.Annotations;
#line default
#line hidden
#line 4 "DatabaseErrorPage.cshtml"
using Microsoft.AspNet.Diagnostics.Entity
#line default
#line hidden
;
#line 5 "DatabaseErrorPage.cshtml"
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
#line default
#line hidden
#line 6 "DatabaseErrorPage.cshtml"
using Microsoft.AspNet.Diagnostics.Entity.Views
#line default
#line hidden
;
using System.Threading.Tasks;
public class DatabaseErrorPage : Microsoft.AspNet.Diagnostics.Views.BaseView
{
#line 14 "DatabaseErrorPage.cshtml"
private DatabaseErrorPageModel _model;
public virtual DatabaseErrorPageModel Model
{
get { return _model; }
[param: NotNull]
set
{
Check.NotNull(value, "value");
_model = value;
}
}
#line default
#line hidden
#line hidden
public DatabaseErrorPage()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
#line 7 "DatabaseErrorPage.cshtml"
Response.StatusCode = 500;
// TODO: Response.ReasonPhrase = "Internal Server Error";
Response.ContentType = "text/html";
Response.ContentLength = null; // Clear any prior Content-Length
#line default
#line hidden
WriteLiteral("\r\n");
WriteLiteral("<!DOCTYPE html>\r\n\r\n<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r" +
"\n <meta charset=\"utf-8\" />\r\n <title>Internal Server Error</title>\r\n <st" +
"yle>\r\n body {\r\n font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;\r\n font-size: .813em;\r\n line-height: 1.4em;\r\n color: #222;\r\n}\r\n\r\nh1, h2, h3, h4, h5 {\r\n font-weight: 100;\r\n}\r\n\r\nh1 {\r\n color: #44525e;\r\n margin: 15px 0 15px 0;\r\n}\r\n\r\nh2 {\r\n margin: 10px 5px 0 0;\r\n}\r\n\r\nh3 {\r\n color: #363636;\r\n margin: 5px 5px 0 0;\r\n}\r\n\r\ncode {\r\n font-family: Consolas, \"Courier New\", courier, monospace;\r\n}\r\n\r\na {\r\n color: #1ba1e2;\r\n text-decoration: none;\r\n}\r\n\r\n a:hover {\r\n color: #13709e;\r\n text-decoration: underline;\r\n }\r\n\r\nhr {\r\n border: 1px #ddd solid;\r\n}\r\n\r\nbody .titleerror {\r\n padding: 3px;\r\n}\r\n\r\n#applyMigrations {\r\n font-size: 14px;\r\n background: #44c5f2;\r\n color: #ffffff;\r\n display: inline-block;\r\n padding: 6px 12px;\r\n margin-bottom: 0;\r\n font-weight: normal;\r\n text-align: center;\r\n white-space: nowrap;\r\n vertical-align: middle;\r\n cursor: pointer;\r\n border: 1px solid transparent;\r\n}\r\n\r\n #applyMigrations:disabled {\r\n background-color: #a9e4f9;\r\n border-color: #44c5f2;\r\n }\r\n\r\n.error {\r\n color: red;\r\n}\r\n\r\n.expanded {\r\n display: block;\r\n}\r\n\r\n.collapsed {\r\n display: none;\r\n}\r\n\r\n ");
#line 37 "DatabaseErrorPage.cshtml"
Write(string.Empty);
#line default
#line hidden
WriteLiteral("\r\n </style>\r\n</head>\r\n<body>\r\n <h1>");
#line 41 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_Title);
#line default
#line hidden
WriteLiteral("</h1>\r\n");
#line 42 "DatabaseErrorPage.cshtml"
#line default
#line hidden
#line 42 "DatabaseErrorPage.cshtml"
if (Model.Options.ShowExceptionDetails)
{
#line default
#line hidden
WriteLiteral(" <p>\r\n");
#line 45 "DatabaseErrorPage.cshtml"
#line default
#line hidden
#line 45 "DatabaseErrorPage.cshtml"
for (Exception ex = Model.Exception; ex != null; ex = ex.InnerException)
{
#line default
#line hidden
WriteLiteral(" <span>");
#line 47 "DatabaseErrorPage.cshtml"
Write(ex.GetType().Name);
#line default
#line hidden
WriteLiteral(": ");
#line 47 "DatabaseErrorPage.cshtml"
Write(ex.Message);
#line default
#line hidden
WriteLiteral("</span>\r\n <br />\r\n");
#line 49 "DatabaseErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </p>\r\n <hr />\r\n");
#line 52 "DatabaseErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral("\r\n");
#line 54 "DatabaseErrorPage.cshtml"
#line default
#line hidden
#line 54 "DatabaseErrorPage.cshtml"
if (!Model.DatabaseExists && !Model.PendingMigrations.Any())
{
#line default
#line hidden
WriteLiteral(" <h2>");
#line 56 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsTitle(Model.ContextType.Name));
#line default
#line hidden
WriteLiteral("</h2>\r\n <p>");
#line 57 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfo);
#line default
#line hidden
WriteLiteral("</p>\r\n <code> ");
#line 58 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_AddMigrationCommand);
#line default
#line hidden
WriteLiteral(" </code>\r\n <br />\r\n <code> ");
#line 60 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommand);
#line default
#line hidden
WriteLiteral(" </code>\r\n <p><strong>");
#line 61 "DatabaseErrorPage.cshtml"
WriteLiteral(Strings.DatabaseErrorPage_EnableMigrationsCommandsInfo);
#line default
#line hidden
WriteLiteral("</strong></p>\r\n <hr />\r\n");
#line 63 "DatabaseErrorPage.cshtml"
}
else if (Model.PendingMigrations.Any())
{
#line default
#line hidden
WriteLiteral(" <div>\r\n <h2>");
#line 67 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_PendingMigrationsTitle(Model.ContextType.Name));
#line default
#line hidden
WriteLiteral("</h2>\r\n <p>");
#line 68 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_PendingMigrationsInfo(Model.ContextType.Name));
#line default
#line hidden
WriteLiteral("</p>\r\n\r\n");
#line 70 "DatabaseErrorPage.cshtml"
#line default
#line hidden
#line 70 "DatabaseErrorPage.cshtml"
if (Model.Options.ListMigrations)
{
#line default
#line hidden
WriteLiteral(" <ul>\r\n");
#line 73 "DatabaseErrorPage.cshtml"
#line default
#line hidden
#line 73 "DatabaseErrorPage.cshtml"
foreach (var migration in Model.PendingMigrations)
{
#line default
#line hidden
WriteLiteral(" <li>");
#line 75 "DatabaseErrorPage.cshtml"
Write(migration);
#line default
#line hidden
WriteLiteral("</li>\r\n");
#line 76 "DatabaseErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral(" </ul>\r\n");
#line 78 "DatabaseErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral("\r\n");
#line 80 "DatabaseErrorPage.cshtml"
#line default
#line hidden
#line 80 "DatabaseErrorPage.cshtml"
if (Model.Options.EnableMigrationCommands)
{
#line default
#line hidden
WriteLiteral(" <p>\r\n <button id=\"applyMigrations\" onclick=\" A" +
"pplyMigrations() \">");
#line 83 "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 91 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsButtonRunning);
#line default
#line hidden
WriteLiteral("\";\r\n\r\n var req = new XMLHttpRequest();\r\n " +
" req.open(\"POST\", \"");
#line 94 "DatabaseErrorPage.cshtml"
Write(Model.Options.MigrationsEndPointPath.Value);
#line default
#line hidden
WriteLiteral("\", true);\r\n var params = \"context=\" + encodeURIComponent(\"" +
"");
#line 95 "DatabaseErrorPage.cshtml"
Write(Model.ContextType.AssemblyQualifiedName);
#line default
#line hidden
WriteLiteral(@""");
req.setRequestHeader(""Content-type"", ""application/x-www-form-urlencoded"");
req.setRequestHeader(""Content-length"", params.length);
req.setRequestHeader(""Connection"", ""close"");
req.onload = function (e) {
if (req.status == 204) {
applyMigrations.innerHTML = """);
#line 102 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsButtonDone);
#line default
#line hidden
WriteLiteral("\";\r\n applyMigrationsSuccess.innerHTML = \"");
#line 103 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_MigrationsAppliedRefresh);
#line default
#line hidden
WriteLiteral(@""";
} else {
ErrorApplyingMigrations();
}
};
req.onerror = function (e) {
ErrorApplyingMigrations();
};
req.send(params);
}
function ErrorApplyingMigrations() {
applyMigrations.innerHTML = """);
#line 117 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsButton);
#line default
#line hidden
WriteLiteral("\";\r\n applyMigrationsError.innerHTML = \"");
#line 118 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsFailed);
#line default
#line hidden
WriteLiteral("\";\r\n applyMigrations.disabled = false;\r\n " +
" }\r\n </script>\r\n");
#line 122 "DatabaseErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral("\r\n <p>");
#line 124 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_HowToApplyFromCmd);
#line default
#line hidden
WriteLiteral("</p>\r\n <code>");
#line 125 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommand);
#line default
#line hidden
WriteLiteral("</code>\r\n <p><strong>");
#line 126 "DatabaseErrorPage.cshtml"
WriteLiteral(Strings.DatabaseErrorPage_EnableMigrationsCommandsInfo);
#line default
#line hidden
WriteLiteral("</strong></p>\r\n <hr />\r\n </div>\r\n");
#line 129 "DatabaseErrorPage.cshtml"
}
else if (Model.PendingModelChanges)
{
#line default
#line hidden
WriteLiteral(" <div>\r\n <h2>");
#line 133 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_PendingChangesTitle(Model.ContextType.Name));
#line default
#line hidden
WriteLiteral("</h2>\r\n <p>");
#line 134 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_PendingChangesInfo);
#line default
#line hidden
WriteLiteral("</p>\r\n <code>");
#line 135 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_AddMigrationCommand);
#line default
#line hidden
WriteLiteral("</code>\r\n <br />\r\n <code>");
#line 137 "DatabaseErrorPage.cshtml"
Write(Strings.DatabaseErrorPage_ApplyMigrationsCommand);
#line default
#line hidden
WriteLiteral("</code>\r\n <p><strong>");
#line 138 "DatabaseErrorPage.cshtml"
WriteLiteral(Strings.DatabaseErrorPage_EnableMigrationsCommandsInfo);
#line default
#line hidden
WriteLiteral("</strong></p>\r\n <hr />\r\n </div>\r\n");
#line 141 "DatabaseErrorPage.cshtml"
}
#line default
#line hidden
WriteLiteral("</body>\r\n</html>");
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,143 @@
@using System
@using System.Linq
@using JetBrains.Annotations;
@using Microsoft.AspNet.Diagnostics.Entity
@using Microsoft.AspNet.Diagnostics.Entity.Utilities;
@using Microsoft.AspNet.Diagnostics.Entity.Views
@{
Response.StatusCode = 500;
// TODO: Response.ReasonPhrase = "Internal Server Error";
Response.ContentType = "text/html";
Response.ContentLength = null; // Clear any prior Content-Length
}
@functions
{
private DatabaseErrorPageModel _model;
public virtual DatabaseErrorPageModel Model
{
get { return _model; }
[param: NotNull]
set
{
Check.NotNull(value, "value");
_model = value;
}
}
}
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Internal Server Error</title>
<style>
<%$ include: ErrorPage.css %>
@string.Empty
</style>
</head>
<body>
<h1>@Strings.DatabaseErrorPage_Title</h1>
@if (Model.Options.ShowExceptionDetails)
{
<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.DatabaseErrorPage_NoDbOrMigrationsTitle(Model.ContextType.Name)</h2>
<p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfo</p>
<code> @Strings.DatabaseErrorPage_AddMigrationCommand </code>
<br />
<code> @Strings.DatabaseErrorPage_ApplyMigrationsCommand </code>
<p><strong>@Strings.DatabaseErrorPage_EnableMigrationsCommandsInfo</strong></p>
<hr />
}
else if (Model.PendingMigrations.Any())
{
<div>
<h2>@Strings.DatabaseErrorPage_PendingMigrationsTitle(Model.ContextType.Name)</h2>
<p>@Strings.DatabaseErrorPage_PendingMigrationsInfo(Model.ContextType.Name)</p>
@if (Model.Options.ListMigrations)
{
<ul>
@foreach (var migration in Model.PendingMigrations)
{
<li>@migration</li>
}
</ul>
}
@if (Model.Options.EnableMigrationCommands)
{
<p>
<button id="applyMigrations" onclick=" ApplyMigrations() ">@Strings.DatabaseErrorPage_ApplyMigrationsButton</button>
<span id="applyMigrationsError" class="error"></span>
<span id="applyMigrationsSuccess"></span>
</p>
<script>
function ApplyMigrations() {
applyMigrations.disabled = true;
applyMigrationsError.innerHTML = "";
applyMigrations.innerHTML = "@Strings.DatabaseErrorPage_ApplyMigrationsButtonRunning";
var req = new XMLHttpRequest();
req.open("POST", "@Model.Options.MigrationsEndPointPath.Value", true);
var params = "context=" + encodeURIComponent("@Model.ContextType.AssemblyQualifiedName");
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", params.length);
req.setRequestHeader("Connection", "close");
req.onload = function (e) {
if (req.status == 204) {
applyMigrations.innerHTML = "@Strings.DatabaseErrorPage_ApplyMigrationsButtonDone";
applyMigrationsSuccess.innerHTML = "@Strings.DatabaseErrorPage_MigrationsAppliedRefresh";
} else {
ErrorApplyingMigrations();
}
};
req.onerror = function (e) {
ErrorApplyingMigrations();
};
req.send(params);
}
function ErrorApplyingMigrations() {
applyMigrations.innerHTML = "@Strings.DatabaseErrorPage_ApplyMigrationsButton";
applyMigrationsError.innerHTML = "@Strings.DatabaseErrorPage_ApplyMigrationsFailed";
applyMigrations.disabled = false;
}
</script>
}
<p>@Strings.DatabaseErrorPage_HowToApplyFromCmd</p>
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommand</code>
<p><strong>@Strings.DatabaseErrorPage_EnableMigrationsCommandsInfo</strong></p>
<hr />
</div>
}
else if (Model.PendingModelChanges)
{
<div>
<h2>@Strings.DatabaseErrorPage_PendingChangesTitle(Model.ContextType.Name)</h2>
<p>@Strings.DatabaseErrorPage_PendingChangesInfo</p>
<code>@Strings.DatabaseErrorPage_AddMigrationCommand</code>
<br />
<code>@Strings.DatabaseErrorPage_ApplyMigrationsCommand</code>
<p><strong>@Strings.DatabaseErrorPage_EnableMigrationsCommandsInfo</strong></p>
<hr />
</div>
}
</body>
</html>

View File

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using JetBrains.Annotations;
using Microsoft.AspNet.Diagnostics.Entity.Utilities;
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Diagnostics.Entity.Views
{
public 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,
bool databaseExists,
bool pendingModelChanges,
[NotNull] IEnumerable<string> pendingMigrations,
[NotNull] 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;
}
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; }
}
}
}

View File

@ -0,0 +1,78 @@
body {
font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
font-size: .813em;
line-height: 1.4em;
color: #222;
}
h1, h2, h3, h4, h5 {
font-weight: 100;
}
h1 {
color: #44525e;
margin: 15px 0 15px 0;
}
h2 {
margin: 10px 5px 0 0;
}
h3 {
color: #363636;
margin: 5px 5px 0 0;
}
code {
font-family: Consolas, "Courier New", courier, monospace;
}
a {
color: #1ba1e2;
text-decoration: none;
}
a:hover {
color: #13709e;
text-decoration: underline;
}
hr {
border: 1px #ddd solid;
}
body .titleerror {
padding: 3px;
}
#applyMigrations {
font-size: 14px;
background: #44c5f2;
color: #ffffff;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-weight: normal;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
}
#applyMigrations:disabled {
background-color: #a9e4f9;
border-color: #44c5f2;
}
.error {
color: red;
}
.expanded {
display: block;
}
.collapsed {
display: none;
}

View File

@ -0,0 +1,24 @@
{
"version": "7.0.0-*",
"description": "ASP.NET 5 Middleware for Entity Framework error pages.",
"compilationOptions": {
"warningsAsErrors": true
},
"dependencies": {
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
"EntityFramework.Migrations": "7.0.0-*"
},
"code": [ "**\\*.cs", "..\\Shared\\*.cs" ],
"frameworks": {
"aspnet50": {
"frameworkAssemblies": {
"System.Configuration": ""
}
},
"aspnetcore50": {
"dependencies": {
"System.Threading.ExecutionContext": "4.0.0-beta-*"
}
}
}
}

View File

@ -0,0 +1,367 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics.Entity.FunctionalTests.Helpers;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.TestHost;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using Microsoft.Data.Entity.SqlServer.FunctionalTests;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Xunit;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class DatabaseErrorPageMiddlewareTest
{
[Fact]
public async Task Successful_requests_pass_thru()
{
TestServer server = TestServer.Create(app => app
.UseDatabaseErrorPage()
.UseMiddleware<SuccessMiddleware>());
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
Assert.Equal("Request Handled", await response.Content.ReadAsStringAsync());
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
class SuccessMiddleware
{
public SuccessMiddleware(RequestDelegate next)
{ }
public virtual async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync("Request Handled");
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
}
[Fact]
public async Task Non_database_exceptions_pass_thru()
{
TestServer server = TestServer.Create(app => app
.UseDatabaseErrorPage()
.UseMiddleware<ExceptionMiddleware>());
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await server.CreateClient().GetAsync("http://localhost/"));
Assert.Equal("Exception requested from TestMiddleware", ex.Message);
}
class ExceptionMiddleware
{
public ExceptionMiddleware(RequestDelegate next)
{ }
public virtual Task Invoke(HttpContext context)
{
throw new InvalidOperationException("Exception requested from TestMiddleware");
}
}
[Fact]
public async Task Error_page_displayed_no_migrations()
{
TestServer server = await SetupTestServer<BloggingContext, NoMigrationsMiddleware>();
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Contains(Strings.DatabaseErrorPage_NoDbOrMigrationsTitle(typeof(BloggingContext).Name), content);
Assert.Contains(Strings.DatabaseErrorPage_AddMigrationCommand.Replace(">", "&gt;"), content);
Assert.Contains(Strings.DatabaseErrorPage_ApplyMigrationsCommand.Replace(">", "&gt;"), content);
}
class NoMigrationsMiddleware
{
public NoMigrationsMiddleware(RequestDelegate next)
{ }
public virtual Task Invoke(HttpContext context)
{
using (var db = context.ApplicationServices.GetService<BloggingContext>())
{
db.Blogs.Add(new Blog());
db.SaveChanges();
throw new Exception("SaveChanges should have thrown");
}
}
}
[Fact]
public async Task Error_page_displayed_pending_migrations()
{
TestServer server = await SetupTestServer<BloggingContextWithMigrations, PendingMigrationsMiddleware>();
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Contains(Strings.DatabaseErrorPage_PendingMigrationsTitle(typeof(BloggingContextWithMigrations).Name), content);
Assert.Contains(Strings.DatabaseErrorPage_ApplyMigrationsCommand.Replace(">", "&gt;"), content);
Assert.Contains("<li>111111111111111_MigrationOne</li>", content);
Assert.Contains("<li>222222222222222_MigrationTwo</li>", content);
Assert.DoesNotContain(Strings.DatabaseErrorPage_AddMigrationCommand.Replace(">", "&gt;"), content);
}
class PendingMigrationsMiddleware
{
public PendingMigrationsMiddleware(RequestDelegate next)
{ }
public virtual Task Invoke(HttpContext context)
{
using (var db = context.ApplicationServices.GetService<BloggingContextWithMigrations>())
{
db.Blogs.Add(new Blog());
db.SaveChanges();
throw new Exception("SaveChanges should have thrown");
}
}
}
[Fact]
public async Task Error_page_displayed_pending_model_changes()
{
TestServer server = await SetupTestServer<BloggingContextWithPendingModelChanges, PendingModelChangesMiddleware>();
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Contains(Strings.DatabaseErrorPage_PendingChangesTitle(typeof(BloggingContextWithPendingModelChanges).Name), content);
Assert.Contains(Strings.DatabaseErrorPage_AddMigrationCommand.Replace(">", "&gt;"), content);
Assert.Contains(Strings.DatabaseErrorPage_ApplyMigrationsCommand.Replace(">", "&gt;"), content);
}
class PendingModelChangesMiddleware
{
public PendingModelChangesMiddleware(RequestDelegate next)
{ }
public virtual Task Invoke(HttpContext context)
{
using (var db = context.ApplicationServices.GetService<BloggingContextWithPendingModelChanges>())
{
var services = (MigrationsDataStoreServices)db.Configuration.DataStoreServices;
services.Migrator.ApplyMigrations();
db.Blogs.Add(new Blog());
db.SaveChanges();
throw new Exception("SaveChanges should have thrown");
}
}
}
[Fact]
public async Task Error_page_then_apply_migrations()
{
TestServer server = await SetupTestServer<BloggingContextWithMigrations, ApplyMigrationsMiddleware>();
var client = server.CreateClient();
var expectedMigrationsEndpoint = "/ApplyDatabaseMigrations";
var expectedContextType = typeof(BloggingContextWithMigrations).AssemblyQualifiedName;
// Step One: Initial request with database failure
HttpResponseMessage response = await client.GetAsync("http://localhost/");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
// Ensure the url we're going to test is what the page is using in it's JavaScript
Assert.Contains("req.open(\"POST\", \"" + expectedMigrationsEndpoint + "\", true);", content);
Assert.Contains("var params = \"context=\" + encodeURIComponent(\"" + expectedContextType + "\");", content);
// Step Two: Request to migrations endpoint
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("context", expectedContextType)
});
response = await client.PostAsync("http://localhost" + expectedMigrationsEndpoint, formData);
content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
// Step Three: Successful request after migrations applied
response = await client.GetAsync("http://localhost/");
content = await response.Content.ReadAsStringAsync();
Assert.Equal("Saved a Blog", content);
}
class ApplyMigrationsMiddleware
{
public ApplyMigrationsMiddleware(RequestDelegate next)
{ }
public virtual async Task Invoke(HttpContext context)
{
using (var db = context.ApplicationServices.GetService<BloggingContextWithMigrations>())
{
db.Blogs.Add(new Blog());
db.SaveChanges();
await context.Response.WriteAsync("Saved a Blog");
}
}
}
[Fact]
public async Task Customize_migrations_end_point()
{
var migrationsEndpoint = "/MyCustomEndPoints/ApplyMyMigrationsHere";
using (var database = await SqlServerTestStore.CreateScratchAsync(createDatabase: false))
{
var server = TestServer.Create(app =>
{
app.UseServices(services =>
{
services.AddEntityFramework().AddSqlServer();
services.AddScoped<BloggingContextWithMigrations>();
services.AddInstance<DbContextOptions>(new DbContextOptions().UseSqlServer(database.Connection.ConnectionString));
});
var options = DatabaseErrorPageOptions.ShowAll;
options.MigrationsEndPointPath = new PathString(migrationsEndpoint);
app.UseDatabaseErrorPage(options);
app.UseMiddleware<PendingMigrationsMiddleware>();
});
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Contains("req.open(\"POST\", \"" + migrationsEndpoint + "\", true);", content);
}
}
[Fact]
public async Task Pass_thru_when_context_not_in_services()
{
using (var database = await SqlServerTestStore.CreateScratchAsync(createDatabase: false))
{
var logProvider = new TestLoggerProvider();
var server = TestServer.Create(app =>
{
app.UseServices(services =>
{
services.AddEntityFramework()
.AddSqlServer();
services.AddInstance<DbContextOptions>(
new DbContextOptions()
.UseSqlServer(database.Connection.ConnectionString));
});
app.UseDatabaseErrorPage();
app.UseMiddleware<ContextNotRegisteredInServicesMiddleware>();
app.ApplicationServices.GetService<ILoggerFactory>().AddProvider(logProvider);
});
var ex = await Assert.ThrowsAsync<SqlException>(async () =>
await server.CreateClient().GetAsync("http://localhost/"));
Assert.True(logProvider.Logger.Messages.Any(m =>
m.StartsWith(Strings.DatabaseErrorPageMiddleware_ContextNotRegistered(typeof(BloggingContext)))));
}
}
class ContextNotRegisteredInServicesMiddleware
{
public ContextNotRegisteredInServicesMiddleware(RequestDelegate next)
{ }
public virtual Task Invoke(HttpContext context)
{
var options = context.ApplicationServices.GetService<DbContextOptions>();
using (var db = new BloggingContext(context.ApplicationServices, options))
{
db.Blogs.Add(new Blog());
db.SaveChanges();
throw new Exception("SaveChanges should have thrown");
}
}
}
[Fact]
public async Task Pass_thru_when_exception_in_logic()
{
using (var database = await SqlServerTestStore.CreateScratchAsync(createDatabase: false))
{
var logProvider = new TestLoggerProvider();
var server = await SetupTestServer<BloggingContextWithSnapshotThatThrows, ExceptionInLogicMiddleware>(logProvider);
var ex = await Assert.ThrowsAsync<SqlException>(async () =>
await server.CreateClient().GetAsync("http://localhost/"));
Assert.True(logProvider.Logger.Messages.Any(m =>
m.StartsWith(Strings.DatabaseErrorPageMiddleware_Exception)));
}
}
class ExceptionInLogicMiddleware
{
public ExceptionInLogicMiddleware(RequestDelegate next)
{ }
public virtual Task Invoke(HttpContext context)
{
using (var db = context.ApplicationServices.GetService<BloggingContextWithSnapshotThatThrows>())
{
db.Blogs.Add(new Blog());
db.SaveChanges();
throw new Exception("SaveChanges should have thrown");
}
}
}
private static async Task<TestServer> SetupTestServer<TContext, TMiddleware>(ILoggerProvider logProvider = null)
where TContext : DbContext
{
using (var database = await SqlServerTestStore.CreateScratchAsync(createDatabase: false))
{
return TestServer.Create(app =>
{
app.UseServices(services =>
{
services.AddEntityFramework()
.AddSqlServer();
services.AddScoped<TContext>();
services.AddInstance<DbContextOptions>(
new DbContextOptions()
.UseSqlServer(database.Connection.ConnectionString));
});
app.UseDatabaseErrorPage();
app.UseMiddleware<TMiddleware>();
if (logProvider != null)
{
app.ApplicationServices.GetService<ILoggerFactory>().AddProvider(logProvider);
}
});
}
}
}
}

View File

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Framework.Logging;
using System.Collections.Generic;
namespace Microsoft.AspNet.Diagnostics.Entity.FunctionalTests.Helpers
{
public class TestLoggerProvider : ILoggerProvider
{
private readonly TestLogger _logger = new TestLogger();
public TestLogger Logger
{
get { return _logger; }
}
public ILogger Create(string name)
{
return _logger;
}
public class TestLogger : ILogger
{
private List<string> _messages = new List<string>();
public IEnumerable<string> Messages
{
get { return _messages; }
}
public void Write(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
{
_messages.Add(formatter(state, exception));
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public IDisposable BeginScope(object state)
{
return NullScope.Instance;
}
public class NullScope : IDisposable
{
public static NullScope Instance = new NullScope();
public void Dispose()
{ }
}
}
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>2f9b479d-8247-4210-804b-78e6dd5c3e98</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,208 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.TestHost;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using Microsoft.Data.Entity.Migrations.Utilities;
using Microsoft.Data.Entity.SqlServer.FunctionalTests;
using Microsoft.Framework.DependencyInjection;
using Xunit;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class MigrationsEndPointMiddlewareTest
{
[Fact]
public async Task Non_migration_requests_pass_thru()
{
TestServer server = TestServer.Create(app => app
.UseMigrationsEndPoint()
.UseMiddleware<SuccessMiddleware>());
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
Assert.Equal("Request Handled", await response.Content.ReadAsStringAsync());
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
class SuccessMiddleware
{
public SuccessMiddleware(RequestDelegate next)
{ }
public virtual async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync("Request Handled");
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
}
[Fact]
public async Task Migration_request_default_path()
{
await Migration_request(useCustomPath: false);
}
[Fact]
public async Task Migration_request_custom_path()
{
await Migration_request(useCustomPath: true);
}
private async Task Migration_request(bool useCustomPath)
{
using (var database = await SqlServerTestStore.CreateScratchAsync(createDatabase: false))
{
var options = new DbContextOptions().UseSqlServer(database.Connection.ConnectionString);
var path = useCustomPath ? new PathString("/EndPoints/ApplyMyMigrations") : MigrationsEndPointOptions.DefaultPath;
TestServer server = TestServer.Create(app =>
{
app.UseServices(services =>
{
services.AddEntityFramework().AddSqlServer();
services.AddScoped<BloggingContextWithMigrations>();
services.AddInstance<DbContextOptions>(options);
});
if (useCustomPath)
{
app.UseMigrationsEndPoint(new MigrationsEndPointOptions { Path = path });
}
else
{
app.UseMigrationsEndPoint();
}
});
using (var db = BloggingContextWithMigrations.CreateWithoutExternalServiceProvider(options))
{
Assert.False(db.Database.AsRelational().Exists());
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("context", typeof(BloggingContextWithMigrations).AssemblyQualifiedName)
});
HttpResponseMessage response = await server.CreateClient()
.PostAsync("http://localhost" + path, formData);
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
Assert.True(db.Database.AsRelational().Exists());
var services = (MigrationsDataStoreServices)db.Configuration.DataStoreServices;
var appliedMigrations = services.Migrator.GetDatabaseMigrations();
Assert.Equal(2, appliedMigrations.Count);
Assert.Equal("111111111111111_MigrationOne", appliedMigrations.ElementAt(0).GetMigrationId());
Assert.Equal("222222222222222_MigrationTwo", appliedMigrations.ElementAt(1).GetMigrationId());
}
}
}
[Fact]
public async Task Context_type_not_specified()
{
var server = TestServer.Create(app =>
{
app.UseMigrationsEndPoint();
});
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>());
var response = await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.StartsWith(Strings.MigrationsEndPointMiddleware_NoContextType, content);
Assert.True(content.Length > 512);
}
[Fact]
public async Task Invalid_context_type_specified()
{
var server = TestServer.Create(app =>
{
app.UseMigrationsEndPoint();
});
var typeName = "You won't find this type ;)";
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("context", typeName)
});
var response = await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.StartsWith(Strings.MigrationsEndPointMiddleware_InvalidContextType(typeName), content);
Assert.True(content.Length > 512);
}
[Fact]
public async Task Context_not_registered_in_services()
{
var server = TestServer.Create(app =>
{
app.UseServices(services =>
{
services.AddEntityFramework().AddSqlServer();
});
app.UseMigrationsEndPoint();
});
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("context", typeof(BloggingContext).AssemblyQualifiedName)
});
var response = await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.StartsWith(Strings.MigrationsEndPointMiddleware_ContextNotRegistered(typeof(BloggingContext)), content);
Assert.True(content.Length > 512);
}
[Fact]
public async Task Exception_while_applying_migrations()
{
using (var database = await SqlServerTestStore.CreateScratchAsync(createDatabase: false))
{
var options = new DbContextOptions().UseSqlServer(database.Connection.ConnectionString);
TestServer server = TestServer.Create(app =>
{
app.UseServices(services =>
{
services.AddEntityFramework().AddSqlServer();
services.AddScoped<BloggingContextWithSnapshotThatThrows>();
services.AddInstance<DbContextOptions>(options);
});
app.UseMigrationsEndPoint();
});
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("context", typeof(BloggingContextWithSnapshotThatThrows).AssemblyQualifiedName)
});
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData));
Assert.Equal(Strings.MigrationsEndPointMiddleware_Exception(typeof(BloggingContextWithSnapshotThatThrows)), ex.Message);
Assert.Equal("Welcome to the invalid snapshot!", ex.InnerException.Message);
}
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using Microsoft.Data.Entity.SqlServer.FunctionalTests;
using System;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class BloggingContext : DbContext
{
protected static readonly string CurrentProductVersion = typeof(HistoryRepository)
.GetTypeInfo()
.Assembly
.GetCustomAttributes<AssemblyInformationalVersionAttribute>()
.Single()
.InformationalVersion;
protected BloggingContext(DbContextOptions options)
: base(options)
{ }
public BloggingContext(IServiceProvider provider, DbContextOptions options)
: base(provider, options)
{ }
public DbSet<Blog> Blogs { get; set; }
}
}

View File

@ -0,0 +1,113 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Migrations;
using Microsoft.Data.Entity.Migrations.Builders;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using System;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class BloggingContextWithMigrations : BloggingContext
{
protected BloggingContextWithMigrations(DbContextOptions options)
: base(options)
{ }
public BloggingContextWithMigrations(IServiceProvider provider, DbContextOptions options)
: base(provider, options)
{ }
// Providing a factory method so that the ctor is hidden from DI
public static BloggingContextWithMigrations CreateWithoutExternalServiceProvider(DbContextOptions options)
{
return new BloggingContextWithMigrations(options);
}
[ContextType(typeof(BloggingContextWithMigrations))]
public class BloggingContextWithMigrationsModelSnapshot : ModelSnapshot
{
public override IModel Model
{
get
{
var builder = new BasicModelBuilder();
builder.Entity("Blogging.Models.Blog", b =>
{
b.Property<int>("BlogId");
b.Property<int>("BlogId").GenerateValueOnAdd();
b.Property<string>("Name");
b.Key("BlogId");
});
return builder.Model;
}
}
}
[ContextType(typeof(BloggingContextWithMigrations))]
public class MigrationOne : Migration, IMigrationMetadata
{
string IMigrationMetadata.MigrationId
{
get { return "111111111111111_MigrationOne"; }
}
string IMigrationMetadata.ProductVersion
{
get { return CurrentProductVersion; }
}
IModel IMigrationMetadata.TargetModel
{
get { return new BloggingContextWithMigrationsModelSnapshot().Model; }
}
public override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable("Blog",
c => new
{
BlogId = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey("PK_Blog", t => t.BlogId);
}
public override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable("Blog");
}
}
[ContextType(typeof(BloggingContextWithMigrations))]
public class MigrationTwo : Migration, IMigrationMetadata
{
string IMigrationMetadata.MigrationId
{
get { return "222222222222222_MigrationTwo"; }
}
string IMigrationMetadata.ProductVersion
{
get { return CurrentProductVersion; }
}
IModel IMigrationMetadata.TargetModel
{
get { return new BloggingContextWithMigrationsModelSnapshot().Model; }
}
public override void Up(MigrationBuilder migrationBuilder)
{ }
public override void Down(MigrationBuilder migrationBuilder)
{ }
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Migrations;
using Microsoft.Data.Entity.Migrations.Builders;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using System;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class BloggingContextWithPendingModelChanges : BloggingContext
{
public BloggingContextWithPendingModelChanges(IServiceProvider provider, DbContextOptions options)
: base(provider, options)
{ }
[ContextType(typeof(BloggingContextWithPendingModelChanges))]
public class BloggingModelSnapshot : ModelSnapshot
{
public override IModel Model
{
get { return new BasicModelBuilder().Model; }
}
}
[ContextType(typeof(BloggingContextWithPendingModelChanges))]
public partial class MigrationOne : Migration, IMigrationMetadata
{
string IMigrationMetadata.MigrationId
{
get { return "111111111111111_MigrationOne"; }
}
string IMigrationMetadata.ProductVersion
{
get { return CurrentProductVersion; }
}
IModel IMigrationMetadata.TargetModel
{
get { return new BasicModelBuilder().Model; }
}
public override void Up(MigrationBuilder migrationBuilder)
{ }
public override void Down(MigrationBuilder migrationBuilder)
{ }
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Migrations;
using Microsoft.Data.Entity.Migrations.Builders;
using Microsoft.Data.Entity.Migrations.Infrastructure;
using System;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class BloggingContextWithSnapshotThatThrows : BloggingContext
{
public BloggingContextWithSnapshotThatThrows(IServiceProvider provider, DbContextOptions options)
: base(provider, options)
{ }
[ContextType(typeof(BloggingContextWithSnapshotThatThrows))]
public class BloggingContextWithSnapshotThatThrowsModelSnapshot : ModelSnapshot
{
public override IModel Model
{
get
{
throw new Exception("Welcome to the invalid snapshot!");
}
}
}
[ContextType(typeof(BloggingContextWithSnapshotThatThrows))]
public class MigrationOne : Migration, IMigrationMetadata
{
string IMigrationMetadata.MigrationId
{
get { return "111111111111111_MigrationOne"; }
}
string IMigrationMetadata.ProductVersion
{
get { return CurrentProductVersion; }
}
IModel IMigrationMetadata.TargetModel
{
get { return new BloggingContextWithSnapshotThatThrowsModelSnapshot().Model; }
}
public override void Up(MigrationBuilder migrationBuilder)
{
throw new Exception("Welcome to the invalid migration!");
}
public override void Down(MigrationBuilder migrationBuilder)
{ }
}
}
}

View File

@ -0,0 +1,16 @@
{
"dependencies": {
"EntityFramework.SqlServer": "7.0.0-*",
"EntityFramework.SqlServer.FunctionalTests": "1.0.0",
"Microsoft.AspNet.Diagnostics.Entity": "7.0.0-*",
"Microsoft.AspNet.Diagnostics.Entity.Tests": "1.0.0",
"Microsoft.AspNet.TestHost": "1.0.0-*",
"Xunit.KRunner": "1.0.0-*"
},
"commands": {
"test": "Xunit.KRunner"
},
"frameworks": {
"aspnet50": { }
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Diagnostics.Entity;
using Microsoft.Data.Entity;
using System.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Diagnostics.EntityTests
{
public class ApiConsistencyTest : ApiConsistencyTestBase
{
protected override Assembly TargetAssembly
{
get { return typeof(DatabaseErrorPageMiddleware).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"
};
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Xunit;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class DatabaseErrorPageOptionsTest
{
[Fact]
public void Default_visibility_is_false()
{
var options = new DatabaseErrorPageOptions();
Assert.False(options.ShowExceptionDetails);
Assert.False(options.ListMigrations);
Assert.False(options.EnableMigrationCommands);
}
[Fact]
public void Default_visibility_can_be_changed()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
Assert.True(options.ShowExceptionDetails);
Assert.True(options.ListMigrations);
Assert.True(options.EnableMigrationCommands);
}
[Fact]
public void ShowExceptionDetails_overides_default_visibility()
{
var options = new DatabaseErrorPageOptions { ShowExceptionDetails = false };
options.SetDefaultVisibility(true);
Assert.False(options.ShowExceptionDetails);
Assert.True(options.ListMigrations);
Assert.True(options.EnableMigrationCommands);
}
[Fact]
public void ListMigrations_overides_default_visibility()
{
var options = new DatabaseErrorPageOptions { ListMigrations = false };
options.SetDefaultVisibility(true);
Assert.True(options.ShowExceptionDetails);
Assert.False(options.ListMigrations);
Assert.True(options.EnableMigrationCommands);
}
[Fact]
public void EnableMigrationCommands_overides_default_visibility()
{
var options = new DatabaseErrorPageOptions { EnableMigrationCommands = false };
options.SetDefaultVisibility(true);
Assert.True(options.ShowExceptionDetails);
Assert.True(options.ListMigrations);
Assert.False(options.EnableMigrationCommands);
}
}
}

View File

@ -0,0 +1,262 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Diagnostics.Entity.Tests.Helpers;
using Microsoft.AspNet.Diagnostics.Entity.Views;
using Microsoft.AspNet.Http;
using Microsoft.Data.Entity;
using Moq;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests
{
public class DatabaseErrorPageTest
{
[Fact]
public async Task No_database_or_migrations_only_displays_scaffold_first_migration()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: false,
pendingModelChanges: false,
pendingMigrations: new string[] { },
options: options);
var content = await ExecutePage(options, model);
AssertHelpers.DisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
AssertHelpers.NotDisplaysApplyMigrations(typeof(BloggingContext), content);
AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
}
[Fact]
public async Task No_database_with_migrations_only_displays_apply_migrations()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: false,
pendingModelChanges: false,
pendingMigrations: new string[] { "111_MigrationOne" },
options: options);
var content = await ExecutePage(options, model);
AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
AssertHelpers.DisplaysApplyMigrations(typeof(BloggingContext), content);
AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
}
[Fact]
public async Task Existing_database_with_migrations_only_displays_apply_migrations()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: true,
pendingModelChanges: false,
pendingMigrations: new string[] { "111_MigrationOne" },
options: options);
var content = await ExecutePage(options, model);
AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
AssertHelpers.DisplaysApplyMigrations(typeof(BloggingContext), content);
AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
}
[Fact]
public async Task Existing_database_with_migrations_and_pending_model_changes_only_displays_apply_migrations()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: true,
pendingModelChanges: true,
pendingMigrations: new string[] { "111_MigrationOne" },
options: options);
var content = await ExecutePage(options, model);
AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
AssertHelpers.DisplaysApplyMigrations(typeof(BloggingContext), content);
AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
}
[Fact]
public async Task Pending_model_changes_only_displays_scaffold_next_migration()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: true,
pendingModelChanges: true,
pendingMigrations: new string[] { },
options: options);
var content = await ExecutePage(options, model);
AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
AssertHelpers.NotDisplaysApplyMigrations(typeof(BloggingContext), content);
AssertHelpers.DisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
}
[Fact]
public async Task Exception_details_are_displayed()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception("Something bad happened"),
databaseExists: false,
pendingModelChanges: false,
pendingMigrations: new string[] { },
options: options);
var content = await ExecutePage(options, model);
Assert.Contains("Something bad happened", content);
}
[Fact]
public async Task Inner_exception_details_are_displayed()
{
var options = new DatabaseErrorPageOptions();
options.SetDefaultVisibility(true);
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);
var content = await ExecutePage(options, model);
Assert.Contains("Something bad happened", content);
Assert.Contains("Because something more badder happened", content);
}
[Fact]
public async Task ShowExceptionDetails_is_respected()
{
var options = new DatabaseErrorPageOptions { ShowExceptionDetails = false };
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception("Something bad happened"),
databaseExists: false,
pendingModelChanges: false,
pendingMigrations: new string[] { },
options: options);
var content = await ExecutePage(options, model);
Assert.DoesNotContain("Something bad happened", content);
}
[Fact]
public async Task ListMigrations_is_respected()
{
var options = new DatabaseErrorPageOptions { ListMigrations = false };
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: true,
pendingModelChanges: false,
pendingMigrations: new string[] { "111_MigrationOne" },
options: options);
var content = await ExecutePage(options, model);
Assert.DoesNotContain("111_MigrationOne", content);
}
[Fact]
public async Task EnableMigrationCommands_is_respected()
{
var options = new DatabaseErrorPageOptions { EnableMigrationCommands = false };
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: true,
pendingModelChanges: false,
pendingMigrations: new string[] { "111_MigrationOne" },
options: options);
var content = await ExecutePage(options, model);
Assert.DoesNotContain(options.MigrationsEndPointPath.Value, content);
}
[Fact]
public async Task MigrationsEndPointPath_is_respected()
{
var options = new DatabaseErrorPageOptions { MigrationsEndPointPath = new PathString("/HitThisEndPoint") };
options.SetDefaultVisibility(true);
var model = new DatabaseErrorPageModel(
contextType: typeof(BloggingContext),
exception: new Exception(),
databaseExists: true,
pendingModelChanges: false,
pendingMigrations: new string[] { "111_MigrationOne" },
options: options);
var content = await ExecutePage(options, model);
Assert.Contains(options.MigrationsEndPointPath.Value, content);
}
private static async Task<string> ExecutePage(DatabaseErrorPageOptions options, DatabaseErrorPageModel model)
{
var page = new DatabaseErrorPage();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
var stream = new MemoryStream();
response.Setup(r => r.Body).Returns(stream);
context.Setup(c => c.Response).Returns(response.Object);
page.Model = model;
await page.ExecuteAsync(context.Object);
var content = Encoding.ASCII.GetString(stream.ToArray());
return content;
}
private class BloggingContext : DbContext
{
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Xunit;
namespace Microsoft.AspNet.Diagnostics.Entity.Tests.Helpers
{
public static class AssertHelpers
{
public static void DisplaysScaffoldFirstMigration(Type contextType, string content)
{
Assert.Contains(Strings.DatabaseErrorPage_NoDbOrMigrationsTitle(contextType.Name), content);
}
public static void NotDisplaysScaffoldFirstMigration(Type contextType, string content)
{
Assert.DoesNotContain(Strings.DatabaseErrorPage_NoDbOrMigrationsTitle(contextType.Name), content);
}
public static void DisplaysApplyMigrations(Type contextType, string content)
{
Assert.Contains(Strings.DatabaseErrorPage_PendingMigrationsTitle(contextType.Name), content);
}
public static void NotDisplaysApplyMigrations(Type contextType, string content)
{
Assert.DoesNotContain(Strings.DatabaseErrorPage_PendingMigrationsTitle(contextType.Name), content);
}
public static void DisplaysScaffoldNextMigraion(Type contextType, string content)
{
Assert.Contains(Strings.DatabaseErrorPage_PendingChangesTitle(contextType.Name), content);
}
public static void NotDisplaysScaffoldNextMigraion(Type contextType, string content)
{
Assert.DoesNotContain(Strings.DatabaseErrorPage_PendingChangesTitle(contextType.Name), content);
}
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>5486117b-a742-49e0-94fc-12b76f061803</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.Data.Entity.Tests
{
public static class TestHelperExtensions
{
public static EntityServicesBuilder AddProviderServices(this EntityServicesBuilder entityServicesBuilder)
{
return entityServicesBuilder.AddInMemoryStore();
}
public static DbContextOptions UseProviderOptions(this DbContextOptions options)
{
return options;
}
}
}

View File

@ -0,0 +1,15 @@
{
"dependencies": {
"EntityFramework.InMemory": "7.0.0-*",
"Microsoft.AspNet.Diagnostics.Entity": "7.0.0-*",
"Moq": "4.2.1312.1622",
"Xunit.KRunner": "1.0.0-*"
},
"code": [ "**\\*.cs", "..\\Shared\\ApiConsistencyTestBase.cs", "..\\Shared\\TestHelpers.cs" ],
"commands": {
"test": "Xunit.KRunner"
},
"frameworks": {
"aspnet50": { }
}
}