c# - mvc - ¿Puede obtener el DbContext de un DbSet?
entity framework database first (4)
¿Por qué haces esto en el DbSet? Intenta hacerlo en el DbContext en su lugar:
public static void AddRangeFast<T>(this DbContext context, IEnumerable<T> items) where T : class
{
var detectChanges = context.Configuration.AutoDetectChangesEnabled;
try
{
context.Configuration.AutoDetectChangesEnabled = false;
var set = context.Set<T>();
foreach (var item in items)
{
set.Add(item);
}
}
finally
{
context.Configuration.AutoDetectChangesEnabled = detectChanges;
}
}
Entonces usarlo es tan simple como:
using (var db = new MyContext())
{
// slow add
db.MyObjects.Add(new MyObject { MyProperty = "My Value 1" });
// fast add
db.AddRangeFast(new[] {
new MyObject { MyProperty = "My Value 2" },
new MyObject { MyProperty = "My Value 3" },
});
db.SaveChanges();
}
En mi aplicación, a veces es necesario guardar 10.000 o más filas en la base de datos en una sola operación. Descubrí que simplemente iterar y agregar cada elemento uno a la vez puede llevar más de media hora.
Sin embargo, si deshabilito AutoDetectChangesEnabled, toma ~ 5 segundos (que es exactamente lo que quiero)
Estoy tratando de hacer un método de extensión llamado "AddRange" a DbSet que deshabilitará AutoDetectChangesEnabled y luego lo volverá a habilitar al finalizar.
public static void AddRange<TEntity>(this DbSet<TEntity> set, DbContext con, IEnumerable<TEntity> items) where TEntity : class
{
// Disable auto detect changes for speed
var detectChanges = con.Configuration.AutoDetectChangesEnabled;
try
{
con.Configuration.AutoDetectChangesEnabled = false;
foreach (var item in items)
{
set.Add(item);
}
}
finally
{
con.Configuration.AutoDetectChangesEnabled = detectChanges;
}
}
Entonces, mi pregunta es: ¿hay una manera de obtener el DbContext desde un DbSet? No me gusta hacer que sea un parámetro, parece que debería ser innecesario.
Con Entity Framework Core (probado con la versión 2.1) puede obtener el contexto actual usando
// DbSet<MyModel> myDbSet
var context = myDbSet.GetService<ICurrentDbContext>().Context;
Cómo obtener un DbContext de un DbSet en EntityFramework Core 2.0
Sí, puede obtener el DbContext
desde un DbSet<TEntity>
, pero la solución es muy pesada. He proporcionado un ejemplo de cómo hacer esto a continuación.
DbContext
el siguiente código y pude recuperar exitosamente la instancia DbContext
desde la cual se generó el DbSet
. Tenga en cuenta que, aunque sí responde a su pregunta, es casi seguro que hay una solución mejor para su problema .
public static class HackyDbSetGetContextTrick
{
public static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet)
where TEntity: class
{
object internalSet = dbSet
.GetType()
.GetField("_internalSet",BindingFlags.NonPublic|BindingFlags.Instance)
.GetValue(dbSet);
object internalContext = internalSet
.GetType()
.BaseType
.GetField("_internalContext",BindingFlags.NonPublic|BindingFlags.Instance)
.GetValue(internalSet);
return (DbContext)internalContext
.GetType()
.GetProperty("Owner",BindingFlags.Instance|BindingFlags.Public)
.GetValue(internalContext,null);
}
}
Ejemplo de uso:
using(var originalContextReference = new MyContext())
{
DbSet<MyObject> set = originalContextReference.Set<MyObject>();
DbContext retrievedContextReference = set.GetContext();
Debug.Assert(ReferenceEquals(retrievedContextReference,originalContextReference));
}
Explicación:
Según Reflector, DbSet<TEntity>
tiene un campo privado _internalSet
de tipo InternalSet<TEntity>
. El tipo es interno a la dll EntityFramework. Hereda de InternalQuery<TElement>
(donde TEntity : TElement
). InternalQuery<TElement>
también es interno a la dll EntityFramework. Tiene un campo privado _internalContext
de tipo InternalContext
. InternalContext
también es interno de EntityFramework. Sin embargo, InternalContext
expone una propiedad pública de DbContext
llamada Owner
. Por lo tanto, si tiene una DbSet<TEntity>
, puede obtener una referencia al propietario de DbContext
, accediendo a cada una de esas propiedades de manera reflexiva y DbContext
resultado final a DbContext
.
Actualización de @LoneyPixel
En EF7 hay un campo privado _context directamente en la clase que implementa DbSet. No es difícil exponer este campo públicamente.
tal vez podría crear un ayudante que deshabilitó esto para usted y luego simplemente llamar al ayudante desde el método AddRange