c# – 我可以克隆一个IQueryable在另一个DbContext的DbSet上运行吗?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 我可以克隆一个IQueryable在另一个DbContext的DbSet上运行吗?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含2988字,纯文字阅读大概需要5分钟。
内容图文
假设我已经通过许多步骤的一些条件逻辑构建了IQueryable< T>.实例我们将调用查询.
我想得到总记录数和一页数据,所以我想调用query.CountAsync()和query.Skip(0).Take(10).ToListAsync().我不能连续调用它们,因为它们都试图同时在同一个DbContext上运行查询时会出现竞争条件.这是不允许的:
“A second operation started on this context before a previous asynchronous operation completed. Use ‘await’ to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.”
我甚至不想在开始第二次之前“等待”第一次.我想尽快解决这两个问题.唯一的方法是从单独的DbContexts运行它们.从DbSet的不同实例开始,我可能必须并排构建整个查询(或2或3),这似乎很荒谬.有没有办法克隆或改变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;
}
}
}
}
与标准EF6 LINQ查询的一个不同之处在于,这产生了ObjectQuery< T>.而不是DbQuery< T>,虽然除了ToString()不返回生成的SQL,我没有注意到进一步的查询构建/执行有任何区别.它似乎有用,但要小心使用它,并且要自担风险.
内容总结
以上是互联网集市为您收集整理的c# – 我可以克隆一个IQueryable在另一个DbContext的DbSet上运行吗?全部内容,希望文章能够帮你解决c# – 我可以克隆一个IQueryable在另一个DbContext的DbSet上运行吗?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。