问题
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