我在过去的几天里搜索了互联网,寻找解决方案,但没有找到我想要的。基本上,这是我的问题:
1. 我需要实现一个接口,该接口具有返回IQueryable的方法(我无法访问接口,因此无法更改此内容)
2. 我希望该方法返回一个连接到非常大的数据库表的IQueryable和在内存中计算的相同实体类型的大型IEnumerable的连接
3. 我不能使用queryableA.Concat(enumerableB).Where(condition),因为它会尝试将整个数组发送到服务器(除此之外,我还会收到只支持原始类型的异常)
4. 我不能使用enumerableB.Concat(queryableA).Where(condition),因为它会将整个表作为IEnumerable加载到内存中处理
我对这个接口没有太多经验,有点不知所措。请问有什么建议吗?如果需要的话,我也可以完全放弃这种方法。
再次说明一下,我遇到了StackOverflowException,堆栈跟踪只是在上面两个注释之间进行了一堆调用,并在每对调用之间添加了“[External Code]”。我已经添加了一个使用两个小枚举类型的示例Main方法,但您可以想象这些数据源是更大的数据源,需要很长时间来枚举。
非常感谢您的帮助!
1. 我需要实现一个接口,该接口具有返回IQueryable的方法(我无法访问接口,因此无法更改此内容)
2. 我希望该方法返回一个连接到非常大的数据库表的IQueryable和在内存中计算的相同实体类型的大型IEnumerable的连接
3. 我不能使用queryableA.Concat(enumerableB).Where(condition),因为它会尝试将整个数组发送到服务器(除此之外,我还会收到只支持原始类型的异常)
4. 我不能使用enumerableB.Concat(queryableA).Where(condition),因为它会将整个表作为IEnumerable加载到内存中处理
所以经过一些搜索,我认为处理这个问题的好方法是编写自己的ConcatenatingQueryable IQueryable实现,它接受两个IQueryable并在每个上独立执行表达式树,然后连接结果。然而,我好像遇到了问题,因为它返回了一个堆栈溢出。根据http://blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx,这是我目前实现的东西:
class Program
{
static void Main(string[] args)
{
var source1 = new[] { 1, 2 }.AsQueryable();
var source2 = new[] { -1, 1 }.AsQueryable();
var matches = new ConcatenatingQueryable<int>(source1, source2).Where(x => x <= 1).ToArray();
Console.WriteLine(string.Join(",", matches));
Console.ReadKey();
}
public class ConcatenatingQueryable<T> : IQueryable<T>
{
private readonly ConcatenatingQueryableProvider<T> provider;
private readonly Expression expression;
public ConcatenatingQueryable(IQueryable<T> source1, IQueryable<T> source2)
: this(new ConcatenatingQueryableProvider<T>(source1, source2))
{}
public ConcatenatingQueryable(ConcatenatingQueryableProvider<T> provider)
{
this.provider = provider;
this.expression = Expression.Constant(this);
}
public ConcatenatingQueryable(ConcatenatingQueryableProvider<T> provider, Expression expression)
{
this.provider = provider;
this.expression = expression;
}
Expression IQueryable.Expression
{
get { return expression; }
}
Type IQueryable.ElementType
{
get { return typeof(T); }
}
IQueryProvider IQueryable.Provider
{
get { return provider; }
}
public IEnumerator<T> GetEnumerator()
{
// This line is calling Execute below
return ((IEnumerable<T>)provider.Execute(expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)provider.Execute(expression)).GetEnumerator();
}
}
public class ConcatenatingQueryableProvider<T> : IQueryProvider
{
private readonly IQueryable<T> source1;
private readonly IQueryable<T> source2;
public ConcatenatingQueryableProvider(IQueryable<T> source1, IQueryable<T> source2)
{
this.source1 = source1;
this.source2 = source2;
}
IQueryable<TS> IQueryProvider.CreateQuery<TS>(Expression expression)
{
var elementType = TypeSystem.GetElementType(expression.Type);
try
{
return (IQueryable<TS>)Activator.CreateInstance(typeof(ConcatenatingQueryable<>).MakeGenericType(elementType), new object[] { this, expression });
}
catch (TargetInvocationException tie)
{
throw tie.InnerException;
}
}
IQueryable IQueryProvider.CreateQuery(Expression expression)
{
var elementType = TypeSystem.GetElementType(expression.Type);
try
{
return (IQueryable)Activator.CreateInstance(typeof(ConcatenatingQueryable<>).MakeGenericType(elementType), new object[] { this, expression });
}
catch (TargetInvocationException tie)
{
throw tie.InnerException;
}
}
TS IQueryProvider.Execute<TS>(Expression expression)
{
return (TS)Execute(expression);
}
object IQueryProvider.Execute(Expression expression)
{
return Execute(expression);
}
public object Execute(Expression expression)
{
// This is where I suspect the problem lies, as executing the
// Expression.Constant from above here will call Enumerate again,
// which then calls this, and... you get the point
dynamic results1 = source1.Provider.Execute(expression);
dynamic results2 = source2.Provider.Execute(expression);
return results1.Concat(results2);
}
}
internal static class TypeSystem
{
internal static Type GetElementType(Type seqType)
{
var ienum = FindIEnumerable(seqType);
if (ienum == null)
return seqType;
return ienum.GetGenericArguments()[0];
}
private static Type FindIEnumerable(Type seqType)
{
if (seqType == null || seqType == typeof(string))
return null;
if (seqType.IsArray)
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
if (seqType.IsGenericType)
{
foreach (var arg in seqType.GetGenericArguments())
{
var ienum = typeof(IEnumerable<>).MakeGenericType(arg);
if (ienum.IsAssignableFrom(seqType))
{
return ienum;
}
}
}
var ifaces = seqType.GetInterfaces();
if (ifaces.Length > 0)
{
foreach (var iface in ifaces)
{
var ienum = FindIEnumerable(iface);
if (ienum != null)
return ienum;
}
}
if (seqType.BaseType != null && seqType.BaseType != typeof(object))
{
return FindIEnumerable(seqType.BaseType);
}
return null;
}
}
}
我对这个接口没有太多经验,有点不知所措。请问有什么建议吗?如果需要的话,我也可以完全放弃这种方法。
再次说明一下,我遇到了StackOverflowException,堆栈跟踪只是在上面两个注释之间进行了一堆调用,并在每对调用之间添加了“[External Code]”。我已经添加了一个使用两个小枚举类型的示例Main方法,但您可以想象这些数据源是更大的数据源,需要很长时间来枚举。
非常感谢您的帮助!
enumerableB.Where(condition).Concat(queryableA.Where(condition))
吗? - Lucas Trzesniewski