最初: 我有几个不同数据库的上下文。它们中的每个都是在特定时间段内的单独数据库。将历史数据与实际数据分开以加快查询速度。
我需要以某种方式统一单个请求的这些上下文。所有请求将像以前一样工作。
为此,我创建了一个本身存储上下文的类,实现了所有Linq方法。在每种方法中,传入的过滤器都将应用于每个上下文中的每个请求。在ToList()
之后,我做了SelectMany(x => x)
。
例如:
位置:
public IFewQueryable<TSource> Where(Expression<Func<TSource, bool>> predicate)
{
var dictionary = new Dictionary<IQueryable<TSource>, DbContext>();
foreach (var (query, context) in QueriesToContexts)
{
dictionary.Add(query.Where(predicate), context);
}
return new FewQueryable<TSource>(dictionary);
}
GroupJoin:
public IFewQueryable<TResult> GroupJoin<TInner, TKey, TResult>(IFewQueryable<TInner> inner, Expression<Func<TSource, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TSource, IEnumerable<TInner>, TResult>> resultSelector)
where TResult : class
where TInner : class
{
var dictionary = new Dictionary<IQueryable<TResult>, DbContext>();
var innerCasted = inner as FewQueryable<TInner>;
for (var i = 0; i < QueriesToContexts.Count; i++)
{
var (queryOriginal, contextOriginal) = QueriesToContexts.Skip(i).First();
dictionary.Add(queryOriginal.GroupJoin(contextOriginal.Set<TInner>(), outerKeySelector, innerKeySelector, resultSelector), contextOriginal);
}
return new FewQueryable<TResult>(dictionary);
}
我有一个问题。在核心3.1中,Union可以在SQL Server上运行,因此我尝试使用impl Union方法。 但通常情况下,Union包含由其他查询组成的查询,因此我无法再使用:
contextOriginal.Set<TInner>()
但是我需要以某种方式在一个上下文中执行请求。
在EF版本6和更早的版本中,可以更改执行上下文,但是据我所知,在EF内核中,没有。所以我尝试通过Expression
来做到这一点:
public IFewQueryable<TSource> Union(IQueryable<TSource> inner)
{
var dictionary = new Dictionary<IQueryable<TSource>, DbContext>();
foreach (var (query, context) in QueriesToContexts)
{
var expression = ConvertQuery(inner.Expression as MethodCallExpression, context);
var newQuery = query.Union(query.Provider.CreateQuery<TSource>(expression));
dictionary.Add(query.Union(newQuery), context);
}
return new FewQueryable<TSource>(dictionary);
}
private MethodCallExpression ConvertQuery(MethodCallExpression expression, DbContext context)
{
var args = new Expression[expression.Arguments.Count];
expression.Arguments.CopyTo(args, 0);
for (var i = 0; i<args.Length; i++)
{
if (args[i].GetType().BaseType == typeof(MethodCallExpression))
{
args[i] = ConvertQuery(args[i] as MethodCallExpression, context);
}
if (args[i].GetType() == typeof(ConstantExpression))
{
var castedExpression = args[i] as ConstantExpression;
var setMethod = typeof(DbContext).GetMethod("Set").MakeGenericMethod(castedExpression.Type.GenericTypeArguments[0]);
var valueDbSet = setMethod.Invoke(context, null);
var asQueryable = typeof(System.Linq.Queryable).GetMethods().Where(x => x.Name == "AsQueryable")
.ToList()[0].MakeGenericMethod(castedExpression.Type.GenericTypeArguments[0]);
var casted = asQueryable.Invoke(null, new[] {valueDbSet });
var constant = Expression.Constant(casted);
args[i] = constant;
}
}
}
但是我得到了例外:
{{query}} by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.Expand(Expression query)
at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expression query)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)