Tri*_*nko 5 c# async-await dbcontext entity-framework-6
假设我已经通过许多步骤中的一些条件逻辑构建了一个IQueryable<T>我们称为的实例query。
我想获取总记录数和一页数据,因此我想致电query.CountAsync()和query.Skip(0).Take(10).ToListAsync()。我不能连续调用它们,因为发生竞争情况时,它们都试图同时DbContext在同一时间运行查询。这是不允许的:
“在先前的异步操作完成之前,第二个操作在此上下文上开始。使用'await'确保在此上下文上调用另一个方法之前所有异步操作都已完成。不保证任何实例成员都是线程安全的。”
我不想在开始第二个之前就先“等待”。我想尽快取消这两个查询。唯一的方法是从单独的DbContext运行它们。我可能不得不从的不同实例开始并排构建整个查询(或2或3),这似乎很荒谬DbSet。是否有任何方法可以克隆或更改IQueryable<T>(不一定是该接口,但它是基础实现),这样我可以有一个副本在DbContext“ A” 上运行,而另一个副本可以在DbContext“ B” 上运行,以便两个查询都可以同时执行?我只是想避免从头开始将查询X重组一次,仅在X上下文上运行它。
没有标准的方法可以做到这一点。问题在于 EF6 查询表达式树包含保存ObjectQuery实例的常量节点,这些实例绑定到创建查询时使用的DbContext(实际上是底层的)。ObjectContext此外,在执行查询之前还会进行运行时检查,看看是否存在绑定到与执行查询不同的上下文的表达式。
我想到的唯一想法是处理查询表达式树,并将ExpressionVisitor这些ObjectQuery实例替换为绑定到新上下文的新实例。
这是上述想法的可能实现:
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
namespace System.Data.Entity
{
public static class DbQueryExtensions
{
public static IQueryable<T> BindTo<T>(this IQueryable<T> source, DbContext target)
{
var binder = new DbContextBinder(target);
var expression = binder.Visit(source.Expression);
var provider = binder.TargetProvider;
return provider != null ? provider.CreateQuery<T>(expression) : source;
}
class DbContextBinder : ExpressionVisitor
{
ObjectContext targetObjectContext;
public IQueryProvider TargetProvider { get; private set; }
public DbContextBinder(DbContext target)
{
targetObjectContext = ((IObjectContextAdapter)target).ObjectContext;
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value is ObjectQuery objectQuery && objectQuery.Context != targetObjectContext)
return Expression.Constant(CreateObjectQuery((dynamic)objectQuery));
return base.VisitConstant(node);
}
ObjectQuery<T> CreateObjectQuery<T>(ObjectQuery<T> source)
{
var parameters = source.Parameters
.Select(p => new ObjectParameter(p.Name, p.ParameterType) { Value = p.Value })
.ToArray();
var query = targetObjectContext.CreateQuery<T>(source.CommandText, parameters);
query.MergeOption = source.MergeOption;
query.Streaming = source.Streaming;
query.EnablePlanCaching = source.EnablePlanCaching;
if (TargetProvider == null)
TargetProvider = ((IQueryable)query).Provider;
return query;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
与标准 EF6 LINQ 查询的一个区别是它生成ObjectQuery<T>而不是DbQuery<T>,尽管除了ToString()不返回生成的 SQL 之外,我没有注意到进一步的查询构建/执行有任何差异。它似乎有效,但请谨慎使用并自行承担风险。
小智 4
您可以编写一个函数来构建查询,并将 DbContext 作为参数。
public IQueryable<T> MyQuery(DbContext<T> db)
{
return db.Table
.Where(p => p.reallycomplex)
....
...
.OrderBy(p => p.manythings);
}
Run Code Online (Sandbox Code Playgroud)
我已经这样做过很多次并且效果很好。
现在可以轻松地使用两个不同的上下文进行查询:
IQueryable<T> q1 = MyQuery(dbContext1);
IQueryable<T> q2 = MyQuery(dbContext2);
Run Code Online (Sandbox Code Playgroud)
如果您关心的是构建 IQueryable 对象所需的执行时间,那么我唯一的建议是不要担心它。
| 归档时间: |
|
| 查看次数: |
300 次 |
| 最近记录: |