你能从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
类型的私有字段_internalContext
。 InternalContext
也是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方法中调用帮助器