.Net EF Core 2.1 Get DbContext from IQueryable argument

爱⌒轻易说出口 提交于 2020-08-07 15:12:23

问题


I have an IQueryable extension method:

public static void SomeExt<T>(this IQueryable<T> query, DbContext context) {...}

and I would like to know if there is some way to get DbContext from query so that DbContext argument could be removed leaving only:

public static void SomeExt<T>(this IQueryable<T> query) {...}

I have tried something like this Access DataContext behind IQueryable but its not working, getting zero fields.

Also there is way to get it from DbSet
Can you get the DbContext from a DbSet?
myDbSet.GetService<'ICurrentDbContext>().Context;
but that's not what I need. I want to get it from Query?

This is the query:
var q = context.Items.Where(a => a.StatusId = 1);

q.SomeExt(context);
vs
q.SomeExt();


回答1:


try this

 public static DbContext GetDbContext(this IQueryable query)
    {
        var compilerField = typeof(EntityQueryProvider).GetField("_queryCompiler", BindingFlags.NonPublic | BindingFlags.Instance);
        var compiler = (QueryCompiler)compilerField.GetValue(query.Provider);

        var queryContextFactoryField = compiler.GetType().GetField("_queryContextFactory", BindingFlags.NonPublic | BindingFlags.Instance);
        var queryContextFactory = (RelationalQueryContextFactory)queryContextFactoryField.GetValue(compiler);


        object stateManagerDynamic;

        var dependenciesProperty = typeof(RelationalQueryContextFactory).GetProperty("Dependencies", BindingFlags.NonPublic | BindingFlags.Instance);
        if (dependenciesProperty != null)
        {
            // EFCore 2.x
            var dependencies = dependenciesProperty.GetValue(queryContextFactory);

            var stateManagerField = typeof(DbContext).GetTypeFromAssembly_Core("Microsoft.EntityFrameworkCore.Query.QueryContextDependencies").GetProperty("StateManager", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            stateManagerDynamic = stateManagerField.GetValue(dependencies);
        }
        else
        {
            // EFCore 1.x
            var stateManagerField = typeof(QueryContextFactory).GetProperty("StateManager", BindingFlags.NonPublic | BindingFlags.Instance);
            stateManagerDynamic = stateManagerField.GetValue(queryContextFactory);
        }

        IStateManager stateManager = stateManagerDynamic as IStateManager;

        if (stateManager == null)
        {
            Microsoft.EntityFrameworkCore.Internal.LazyRef<IStateManager> lazyStateManager = stateManagerDynamic as Microsoft.EntityFrameworkCore.Internal.LazyRef<IStateManager>;
            if (lazyStateManager != null)
            {
                stateManager = lazyStateManager.Value;
            }
        }

        if (stateManager == null)
        {
            stateManager = ((dynamic)stateManagerDynamic).Value;
        }


        return stateManager.Context;
    }



回答2:


Sounds like you want to implement ActiveRecord in Entity Framework. Many have tried... Best I can suggest is make your context.Items property something LINQ-like that bootlegs the context, e.g:

public class MyContext : DbContext
{
    QueryableWithContext<Item> Items {get => new QueryableWithContext<Item>(ItemsSet, this)}
    private DbSet<Item> ItemsSet {get;set;}
}

public class QueryableWithContext<T>
{
    public DbContext Context { get; }
    private IQueryable<T> inner;

    public QueryableWithContext(IQueryable<T> inner, DbContext context)
    {
        this.inner = inner;
        this.Context = context;
    }

    public QueryableWithContext<T> Where(Func<T,bool> predicate)
    {
        return new QueryableWithContext<T>(inner.Where(predicate) as IQueryable<T>, Context);
    }

    // plus lots of other LINQ-like expressions
}

Then your extension method is not on IQueryable<T> but on QueryableWithContext<T>, and can access the Context property.




回答3:


I have found a way to do this

public static DbContext GetDbContext(IQueryable query)
{
    var bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
    var queryCompiler = typeof(EntityQueryProvider).GetField("_queryCompiler", bindingFlags).GetValue(query.Provider);
    var queryContextFactory = queryCompiler.GetType().GetField("_queryContextFactory", bindingFlags).GetValue(queryCompiler);

    var dependencies = typeof(RelationalQueryContextFactory).GetProperty("Dependencies", bindingFlags).GetValue(queryContextFactory);
    var queryContextDependencies = typeof(DbContext).Assembly.GetType(typeof(QueryContextDependencies).FullName);
    var stateManagerProperty = queryContextDependencies.GetProperty("StateManager", bindingFlags | BindingFlags.Public).GetValue(dependencies);
    var stateManager = (IStateManager)stateManagerProperty;

    return stateManager.Context;
}

For EFCore 3 instead of

.GetProperty("Dependencies", bindingFlags)

use

.GetField("_dependencies", bindingFlags)


来源:https://stackoverflow.com/questions/53198376/net-ef-core-2-1-get-dbcontext-from-iqueryable-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!