2013-08-22 更新:
在查看了“构建 IQueryable 提供程序系列”之后(感谢您的链接!),我进一步了解了一些。我相应地更新了代码。它仍然没有完全工作。如果我正确理解了本教程,GetEnumerator
则会在请求多个元素的情况下调用 (例如,通过ToList()
调用可查询或任何聚合函数)。因此GetEnumerator
,包装器的所有实现所要做的就是Execute
在提供者上调用 an 并传递可查询的表达式。在另一种情况下,如果只请求单个元素,Execute
则直接调用。可查询的表达式还反映了它是针对单个元素还是多个元素。这个对吗?
不幸的是,现在我在调用源查询提供程序时收到一个 InvalidOperationException 说“序列包含多个元素” 。Execute
这是什么意思?我只是传递表达式而没有任何翻译,因为如上所述涉及相同的类型。代码中的翻译位IEnumerable
可能不完整,但现在我什至没有达到这一点。
我正在尝试使用单个底层 IQueryable 作为数据源来实现一个简单的 IQueryable 包装器,该数据源为每个结果对象调用一个翻译函数。
我认为这将是相对微不足道的,因为包装器唯一要做的就是翻译。但是我无法让我的实现工作。
到目前为止,请参阅下面的内容。对于某些查询,它正在工作,但我在某些时候收到了StackOverflowException InvalidOperationException。我猜这是由于我的可查询对象和我的查询提供者之间的循环关联而发生的。但我不明白如何正确实现这一点。
这是我的问题和想法:
1. 为什么IQueryable 有一个Provider 又返回一个IQueryable?这不是需要无限递归吗?
2、为什么实现IEnumerator还不够?为什么 FirstOrDefault 例如不使用枚举器来获取元素?当我在我的可查询对象上调试应用程序 GetEnumerator() 时,FirstOrDefault() 没有调用它。
3. 由于枚举器不是在所有情况下都使用,那么调用翻译函数的正确点在哪里?QueryProvider 的执行方法似乎是正确的地方。但是在某些情况下,我是否仍然需要枚举器中的翻译调用? 更新:我知道我需要提供我自己的IEnumerable
实现来提供并从我的方法中TranslatingEnumerator
返回这个可枚举。Execute
为了获得枚举器GetEnumerator
调用Execute
(见下文)。请求枚举器的 LINQ 代码似乎确保表达式实际上返回一个IEnumerable
.
关于代码的一些旁注:
翻译源类型命名为TDatabaseEntity,翻译目标类型命名为TBusinessEntity。
我本质上提供了一个 IQueryable,它获取从底层 IQueryable 检索的结果对象并将它们转换为 TBusinessEntity
类型的对象。我知道表达式也需要翻译。但是我把它放在一边,因为在我的实际应用程序中,我对 TBusinessEntity 和 TDatabaseEntity 使用相同的类型,因此可以直接传递表达式。
尽管结果对象属于同一类型,但仍需要将其转换为其他实例。更新:我的翻译层已经在我的应用程序中工作,并且还负责相关实体。这只是我坚持的“实现 IQueryable 包装器”的事情。
恐怕整个实现都不正确——代码中的 TODO 只是我自己的笔记。
背景:我有点在我的数据访问层中实现我自己的从 DbContext 接收的实体的分离,以防止我的业务层与实际实体取得联系——由于 EF 和其他要求的一些错误,我不能直接使用 EF 分离实体。
谢谢你的帮助!
可查询的实现
internal class TranslatingQueryable<TDatabaseEntity, TBusinessEntity> : IQueryable<TBusinessEntity>
{
private readonly IQueryProvider _provider;
private readonly IQueryable<TDatabaseEntity> _source;
internal TranslatingQueryable(TranslatingQueryProvider provider, IQueryable<TDatabaseEntity> source)
{
Guard.ThrowIfArgumentNull(provider, "provider");
Guard.ThrowIfArgumentNull(source, "source");
_provider = provider;
_source = source;
}
internal TranslatingQueryable(Func<object, object> translateFunc, IQueryable<TDatabaseEntity> databaseQueryable)
: this(new TranslatingQueryProvider(translateFunc, databaseQueryable.Provider), databaseQueryable)
{
}
public IEnumerator<TBusinessEntity> GetEnumerator()
{
return ((IEnumerable<TBusinessEntity>)Provider.Execute(Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
}
public Expression Expression
{
get
{
return _source.Expression;
}
}
public Type ElementType
{
get
{
return typeof(TBusinessEntity);
}
}
public IQueryProvider Provider
{
get
{
return _provider;
}
}
}
IQueryProvider 实现
public class TranslatingQueryProvider : IQueryProvider
{
private readonly Func<object, object> _translateFunc;
private readonly IQueryProvider _databaseQueryProvider;
public TranslatingQueryProvider(Func<object, object> translateFunc, IQueryProvider databaseQueryProvider)
{
_translateFunc = translateFunc;
_databaseQueryProvider = databaseQueryProvider;
}
public IQueryable CreateQuery(Expression expression)
{
var databaseQueryable = _databaseQueryProvider.CreateQuery<object>(expression);
return new TranslatingQueryable<object, object>(this, databaseQueryable);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
var databaseQueryable = _databaseQueryProvider.CreateQuery<object>(expression);
return new TranslatingQueryable<object, TElement>(this, databaseQueryable);
}
public object Execute(Expression expression)
{
return Execute<object>(expression);
}
public TResult Execute<TResult>(Expression expression)
{
// TODO This call throws an InvalidOperationException if an enumeration is requested
var databaseResult = _databaseQueryProvider.Execute<TResult>(expression);
var databaseEnumerable = databaseResult as IEnumerable;
if (databaseEnumerable != null)
{
if (typeof(TResult).IsAssignableFrom(typeof(IEnumerable)))
{
throw new InvalidOperationException();
}
return (TResult)(object)new TranslatingEnumerable(databaseEnumerable, _translateFunc);
}
else
{
return (TResult)_translateFunc(databaseResult);
}
}
private class TranslatingEnumerable : IEnumerable
{
private readonly TranslatingEnumerator _enumerator;
public TranslatingEnumerable(IEnumerable databaseEnumerable, Func<object, object> translateFunc)
{
_enumerator = new TranslatingEnumerator(translateFunc, databaseEnumerable.GetEnumerator());
}
public IEnumerator GetEnumerator()
{
return _enumerator;
}
}
}
IEnumerator 实现
internal class TranslatingEnumerator : IEnumerator
{
private readonly Func<object, object> _translateFunc;
private readonly IEnumerator _databaseEnumerator;
internal TranslatingEnumerator(Func<object, object> translateFunc, IEnumerator databaseEnumerator)
{
_translateFunc = translateFunc;
_databaseEnumerator = databaseEnumerator;
}
public bool MoveNext()
{
return _databaseEnumerator.MoveNext();
}
public void Reset()
{
_databaseEnumerator.Reset();
}
public object Current
{
get
{
return _translateFunc(_databaseEnumerator.Current);
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
}