你能从DbSet获得DbContext吗?

在我的应用程序中,有时需要在一次操作中将10,000行或更多行保存到数据库中。 我发现,简单地迭代并逐个添加每个项目可能需要花费半个多小时。

但是,如果我禁用AutoDetectChangesEnabled它需要约5秒(这正是我想要的)

我正在尝试向DbSet创建一个名为“AddRange”的扩展方法,该方法将禁用AutoDetectChangesEnabled,然后在完成时重新启用它。

public static void AddRange(this DbSet set, DbContext con, IEnumerable 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? 我不喜欢把它作为参数 – 感觉它应该是不必要的。

是的, 您可以从DbSet获取DbContext ,但解决方案reflection很重。 我已经提供了一个如何在下面执行此操作的示例。

我测试了以下代码,它能够成功检索生成DbContext实例。 请注意,虽然它确实回答了您的问题, 但几乎可以肯定是您的问题的更好解决方案

 public static class HackyDbSetGetContextTrick { public static DbContext GetContext(this DbSet 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); } } 

用法示例:

 using(var originalContextReference = new MyContext()) { DbSet set = originalContextReference.Set(); DbContext retrievedContextReference = set.GetContext(); Debug.Assert(ReferenceEquals(retrievedContextReference,originalContextReference)); } 

说明:

根据Reflector, DbSet有一个DbSet类型的私有字段_internalSet 。 该类型是EntityFramework dll的内部类型。 它inheritance自InternalQuery (其中TEntity : TElement )。 InternalQuery也是EntityFramework dll的内部。 它有一个InternalContext类型的私有字段_internalContextInternalContext也是EntityFramework的内部。 但是, InternalContext公开名为Owner的公共DbContext属性。 因此,如果你有一个DbSet ,你可以通过DbContext访问每个属性并将最终结果转换为DbContext来获得对DbContext所有者的引用。

从@LoneyPixel更新

在EF7中,实现DbSet的类中直接有一个私有字段_context。 公开揭露这一领域并不难

你为什么在DbSet上这样做? 尝试在DbContext上执行此操作:

 public static void AddRangeFast(this DbContext context, IEnumerable items) where T : class { var detectChanges = context.Configuration.AutoDetectChangesEnabled; try { context.Configuration.AutoDetectChangesEnabled = false; var set = context.Set(); 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(); } 

使用Entity Framework Core(使用2.1版测试),您可以使用获取当前上下文

 // DbSet myDbSet var context = myDbSet.GetService().Context; 

如何从EntityFramework Core 2.0中的DbSet获取DbContext

也许你可以为你创建一个禁用它的帮助器,然后从AddRange方法中调用帮助器