在我的应用程序中,有时需要在一次操作中将10,000行或更多行保存到数据库中。我发现,简单地迭代并一次添加一个项目可能需要半个多小时。
但是,如果我禁用AutoDetectChangesEnabled,则需要约5秒钟(这正是我想要的)
我正在尝试向DbSet创建一个名为“AddRange”的扩展方法,该方法将禁用AutoDetectChangesEnabled,然后在完成时重新启用它。
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;
}
}
所以,我的问题是:有没有办法从DbSet获取DbContext?我不喜欢把它作为一个参数 - 感觉它应该是不必要的。
答案 0 :(得分:20)
是的,您可以从DbContext
获得DbSet<TEntity>
,但解决方案很重要。我在下面提供了一个如何执行此操作的示例。
我测试了以下代码,它能够成功检索生成DbContext
的{{1}}实例。请注意,尽管它确实回答了您的问题,几乎可以肯定是您的问题的更好解决方案。
DbSet
使用示例:
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);
}
}
<强>解释强>
根据Reflector,using(var originalContextReference = new MyContext())
{
DbSet<MyObject> set = originalContextReference.Set<MyObject>();
DbContext retrievedContextReference = set.GetContext();
Debug.Assert(ReferenceEquals(retrievedContextReference,originalContextReference));
}
有一个DbSet<TEntity>
类型的私有字段_internalSet
。该类型是EntityFramework dll的内部类型。它继承自InternalSet<TEntity>
(其中InternalQuery<TElement>
)。 TEntity : TElement
也是EntityFramework dll的内部。它有一个InternalQuery<TElement>
类型的私有字段_internalContext
。 InternalContext
也是EntityFramework的内部信息。但是,InternalContext
会公开名为InternalContext
的公共DbContext
属性。因此,如果您有Owner
,则可以通过反复访问每个属性并将最终结果转换为DbSet<TEntity>
来获取对DbContext
所有者的引用。
在EF7中,实现DbSet的类中直接有一个私有字段_context。公开披露此字段并不难
答案 1 :(得分:14)
你为什么在DbSet上这样做?尝试在DbContext上执行此操作:
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;
}
}
然后使用它就像:
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();
}
答案 2 :(得分:11)
使用Entity Framework Core(使用2.1版测试),您可以使用
获取当前上下文// DbSet<MyModel> myDbSet
var context = myDbSet.GetService<ICurrentDbContext>().Context;
How to get a DbContext from a DbSet in EntityFramework Core 2.0
答案 3 :(得分:0)
也许您可以创建一个帮助您禁用此功能的帮助程序,然后从AddRange方法中调用帮助程序