從實體框架核心中的實體獲取DbContext

c# entity-framework entity-framework-core reflection

有沒有辦法獲取DbContext的實例正在跟踪實體(如果有的話)?

從實體框架中的實體中找到了EF6 Get DbContext的以下建議/解決方案

public static DbContext GetDbContextFromEntity(object entity)
{
    var object_context = GetObjectContextFromEntity( entity );

    if ( object_context == null )
        return null;

    return new DbContext( object_context, dbContextOwnsObjectContext: false );
}

private static ObjectContext GetObjectContextFromEntity(object entity)
{
    var field = entity.GetType().GetField("_entityWrapper");

    if ( field == null )
        return null;

    var wrapper  = field.GetValue(entity);
    var property = wrapper.GetType().GetProperty("Context");
    var context  = (ObjectContext)property.GetValue(wrapper, null);

    return context;
}

有沒有辦法在EF Core中獲得此結果?

一般承認的答案

不,EF Core還沒有延遲加載。如果有的話,那麼從它生成的代理最終會引用加載它的DbContext。截至目前,沒有這樣的參考。


熱門答案

沒有好辦法做到這一點。在構造實體對象之後但在通過調用代碼枚舉之前,似乎沒有簡單的方法將任何代碼注入到進程中。

Subclassing InternalDbSet是我考慮過的,但你只能修復對.Find方法的調用,而IQueryable實現(你使用DbSet的主要方式)是遙不可及的。

因此,我唯一能看到的選項是不允許訪問DbSet,但是具有訪問器功能,這將為我設置.Owner(或任何你想要調用的)屬性。這很麻煩,因為你通常必須為你想要的每個查詢類型編寫一個函數,並且調用者不能再使用LINQ了。但是我們可以使用泛型和回調來保持大部分的靈活性,儘管它看起來很難看。這就是我想出的。

我正在努力移植和清理複雜的系統,所以我還沒有真正測試這個,但這個概念是合理的。代碼可能需要進一步調整才能按需工作。這不應該有任何懲罰,例如,只要你使用EnumerateEntities枚舉而不是QueryEntities,在處理任何記錄之前拉下整個表,但我還沒有對此進行任何真正的測試。

    private void InitEntity(Entity entity) {
        if (entity == null) {
            return;
        }
        entity.Owner = this;
        // Anything you want to happen goes here!
    }
    private DbSet<Entity> Entities { get; set; }
    public IEnumerable<Entity> EnumerateEntities() {
        foreach (Entity entity in this.Entities) {
            this.InitEntity(entity);
            yield return entity;
        }
    }
    public IEnumerable<Entity> EnumerateEntities(Func<DbSet<Entity>, IEnumerable<Entity>> filter) {
        IEnumerable<Entity> ret = filter(this.Entities);
        foreach (Entity entity in ret) {
            this.InitEntity(entity);
            yield return entity;
        }
    }
    public T QueryEntities<T>(Func<DbSet<Entity>, T> filter) {
        if (filter is Func<DbSet<Entity>, Entity>) {
            T ret = filter(this.Entities);
            this.InitEntity(ret as Entity);
            return ret;
        }

        if (filter is Func<DbSet<Entity>, IEnumerable<Entity>>) {
            IEnumerable<Entity> ret = filter(this.Entities) as IEnumerable<Entity>;
            // You should be using EnumerateEntities, this will prefetch all results!!! Can't be avoided, we can't mix yield and no yield in the same function.
            return (T)ret.Select(x => {
                this.InitEntity(x);
                return x;
            });
        }

        return filter(this.Entities);
    }
    public void QueryEntities(Action<DbSet<Entity>> filter) => filter(this.Entities);


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow