簡體   English   中英

你能從DbSet獲得DbContext嗎?

[英]Can you get the DbContext from a DbSet?

在我的應用程序中,有時需要在一次操作中將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? 我不喜歡把它作為參數 - 感覺它應該是不必要的。

是的, 您可以從DbSet<TEntity>獲取DbContext ,但解決方案反射很重。 我在下面提供了一個如何執行此操作的示例。

我測試了以下代碼,並能夠成功獲取DbContext從實例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); 
    } 
}

用法示例:

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

說明:

根據Reflector, DbSet<TEntity>有一個類型為InternalSet<TEntity>的私有字段_internalSet 該類型是EntityFramework dll的內部類型。 它繼承自InternalQuery<TElement> (其中TEntity : TElement )。 InternalQuery<TElement>也是EntityFramework dll的內部。 它有一個InternalContext類型的私有字段_internalContext InternalContext也是EntityFramework的內部。 但是, InternalContext公開名為Owner的公共DbContext屬性。 因此,如果你有一個DbSet<TEntity> ,你可以通過DbContext訪問每個屬性並將最終結果轉換為DbContext來獲得對DbContext所有者的引用。

@LoneyPixel更新

在EF7中,實現DbSet的類中直接有一個私有字段_context。 公開披露這個領域並不難

你為什么在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();
}

使用Entity Framework Core(使用2.1版測試),您可以使用獲取當前上下文

// DbSet<MyModel> myDbSet
var context = myDbSet.GetService<ICurrentDbContext>().Context;

如何從EntityFramework Core 2.0中的DbSet獲取DbContext

也許你可以為你創建一個禁用它的幫助器,然后從AddRange方法中調用幫助器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM