c# - Usar Mini-Profilier con EF 4.3 y MVC 4 sin crear la base de datos
asp.net-mvc entity-framework (4)
Esta excepción ocurre cuando pierdo una configuración para Miniprofiler.
Posibles casos:
Falta incluir en tu etiqueta de diseño
@ MvcMiniProfiler.MiniProfiler.RenderIncludes ()
Falta "MiniProfiler.cs" en tu carpeta App_Start.
Llamada perdida en la función Application_Start ()
AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); BundleTable.Bundles.RegisterTemplateBundles(); MiniProfilerEF.Initialize();
Probado con mvc4, Ef 4.3 para la base de datos existente.
Tengo un problema en el que utilizamos EF 4.3 Code First en una base de datos existente. Quiero usar el Mini-Profiler con EF y llamar
MvcMiniProfiler.MiniProfilerEF.Initialize();
Sin embargo, dado que en realidad no creamos ninguna de las tablas, las tablas dbo .__ MigrationHistory y dbo.EdmMetadata no existen. El generador de perfiles termina colgando porque no existen. ¿Hay alguna forma de que el generador de perfiles ignore estas tablas específicas del Código EF? ¡Gracias!
EDITAR:
Estas son las excepciones que recibo: (Vienen por separado)
Invalid object name ''dbo.__MigrationHistory''.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
at MvcMiniProfiler.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior) in /mvc-mini-profiler/MvcMiniProfiler/Data/ProfiledDbCommand.cs:line 155
at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)
Invalid object name ''dbo.EdmMetadata''.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
at MvcMiniProfiler.Data.ProfiledDbCommand.ExecuteDbDataReader(CommandBehavior behavior) in /mvc-mini-profiler/MvcMiniProfiler/Data/ProfiledDbCommand.cs:line 155
at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)
Comencé un nuevo proyecto MVC 4 e instalé / actualicé los siguientes paquetes NuGet:
- Marco de la entidad
- MiniProfiler
- MiniProfiler.EF
Desactivé la estrategia de inicialización de la base de datos en Code First dentro del contexto de mi base de datos.
public class EmployeeContext : DbContext
{
static EmployeeContext()
{
Database.SetInitializer<EmployeeContext>( null ); // must be turned off before mini profiler runs
}
public IDbSet<Employee> Employees { get; set; }
}
El mini perfilador funciona correctamente. Creé la base de datos de una tabla a mano.
Desactivar el inicializador de la base de datos en el constructor estático es importante. Si lo hace en otro lugar, es posible que el código del mini perfilador se ejecute antes que su código y, por lo tanto, las consultas a la tabla __MigrationHistory que no deberían estar ocurriendo en absoluto.
El problema:
Si se inicializa MiniProfiler antes de que se ejecuten las estrategias de inicialización de nuestra base de datos de Entity Framework, la inicialización falla con un error sobre una tabla de migración faltante.
Si las estrategias de inicialización de la base de datos de Entity Framework se ejecutan primero, el acceso a las entidades falla con una excepción de conversión de tipo ya que el MiniProfiler DbConnection se intenta forzar en una variable SqlConnection (en un genérico interno).
La causa:
Cuando MiniProfiler se inicializa, utiliza la reflexión para recuperar una colección de proveedores de bases de datos de un campo estático privado en System.Data.Common.DbProviderFactories. A continuación, reescribe esta lista con los proveedores de shim MiniProfiler para reemplazar los proveedores nativos. Esto permite a MiniProfiler interceptar cualquier llamada a la base de datos de forma silenciosa.
Cuando Entity Framework se inicializa, comienza a compilar los modelos de datos y crea bases de datos inicializadas en caché almacenadas en System.Data.Entity.Internal.LazyInternalContext dentro de algunos campos estáticos privados. Una vez que se crean, las consultas en DbContext usan los modelos almacenados en caché y las bases de datos internamente tipadas para usar los proveedores que existían en el momento de la inicialización.
Cuando se ejecuta la estrategia de inicialización de la base de datos de Entity Framework, necesita acceso al proveedor Sql simple y nativo, no al shim MiniProfiler, para generar correctamente el SQL para crear tablas. Pero una vez que se hacen estas llamadas al proveedor nativo, el proveedor nativo se almacena en caché en LazyInternalContext y ya no podemos inyectar los suplementos de MiniFilder sin fallas en el tiempo de ejecución.
Mi solución:
Acceda a las colecciones privadas dentro de System.Data.Entity.Internal.LazyInternalContext y elimine los modelos compilados en caché y las bases de datos inicializadas.
Si realizo esta purga entre el funcionamiento de las estrategias de inicialización de la base de datos EF y la inicialización de MiniProfiler, las cuñas MiniProfiler pueden insertarse sin causar fallas posteriores en el tiempo de ejecución.
Código: Este código me funcionó:
Type type = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.LazyInternalContext");
object concurrentDictionary = (type.GetField("InitializedDatabases", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);
var initializedDatabaseCache = (IDictionary)concurrentDictionary;
if (initializedDatabaseCache != null) initializedDatabaseCache.Clear();
object concurrentDictionary2 = (type.GetField("CachedModels", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);
var modelsCache = (IDictionary)concurrentDictionary2;
if (modelsCache != null) modelsCache.Clear();
Advertencia:
Parece que los nombres de los campos internos en LazyInternalContext cambian entre las versiones de EF, por lo que es posible que deba modificar este código para que funcione con la versión exacta de EF que incluye en su proyecto.
Encontré un problema adicional de "pirateo" para deshabilitar la inicialización de la base de datos de EntityFramework (si no se requiere). DefaultInitializer para DB debe establecerse en nulo antes de inicializar contextos db y MiniProfiler
Type type = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.LazyInternalContext");
var field = type.GetField("DefaultCodeFirstInitializer", BindingFlags.NonPublic | BindingFlags.Static);
if (field != null)
field.SetValue(null, null);
else
{
var field2 = type.GetField("_defaultCodeFirstInitializer", BindingFlags.NonPublic | BindingFlags.Static);
if (field2 != null)
field2.SetValue(null, null);
}
Por lo tanto, resolverá los problemas con las dbo.EdmMetadata
y dbo.__MigrationHistory