cya*_*902 16 .net c# linq entity-framework iqueryable
更新2013-08-22:
看了"建立一个IQueryable提供者系列"(感谢链接!)后,我进一步了解了一下.我相应地更新了代码.它仍然没有完全工作.如果我正确理解了教程,GetEnumerator
则在请求多个元素的情况下调用(例如,通过ToList()
调用queryable或任何聚合函数).所以GetEnumerator
包装器的所有实现都要Execute
在提供者上调用一个并传递可查询的表达式.在另一种情况下,如果只请求一个元素,Execute
则直接调用.queryable的表达式还反映了它是单个元素还是多个元素.它是否正确?
不幸的是,现在我在调用源查询提供程序时得到一个InvalidOperationException,说"Sequence包含多个元素"Execute
.这是什么意思?我只是传递表达式而没有任何翻译,因为如上所述涉及相同的类型.IEnumerable
代码中的翻译位可能不完整,但是现在我甚至没有达到这一点.
我正在尝试使用单个底层IQueryable作为数据源来实现一个简单的IQueryable包装器,该数据源为每个结果对象调用转换函数.
我认为这将是相对微不足道的,因为包装器唯一要做的就是翻译.但是我无法让我的实现工作.
请看下面我到目前为止所得到的内容.对于某些查询,它正在工作,但我在某些时候收到StackOverflowException InvalidOperationException.我想这是因为我的queryable和我的查询提供程序之间的循环关联.但我不明白如何正确实现这一点.
在这里我的问题和想法:
1.为什么IQueryable有一个Provider又可以再次返回IQueryable?这不是要求无休止的递归吗?
2.为什么不能实现IEnumerator?为什么FirstOrDefault不使用枚举器来获取元素?当我调试应用程序时GetEnumerator()没有被我的可查询器上的FirstOrDefault()调用.
3.由于在每种情况下都没有使用枚举器,因此调用翻译函数的正确位置在哪里?QueryProvider的Execute-methods似乎是正确的地方.但在某些情况下,我仍然需要在枚举器中进行翻译调用吗? 更新:我知道我意识到我需要提供自己的IEnumerable
实现,TranslatingEnumerator
并从我的Execute
方法返回这个枚举.为了获得枚举器GetEnumerator
调用Execute
(见下文).请求枚举器的LINQ代码似乎确保表达式实际返回一个IEnumerable
.
关于代码的一些附注:
翻译源类型名为TDatabaseEntity,翻译目标类型名为TBusinessEntity.
我基本上提供了一个IQueryable,它从基础IQueryable中获取结果对象,并将它们转换为TBusinessEntity 类型 对象.
我知道表达式也需要翻译.但是我将它放在一边,因为在我的实际应用程序中,我使用相同类型的TBusinessEntity和TDatabaseEntity,因此Expression可以直接传递.
尽管属性相同,但结果对象仍然需要转换为其他实例.更新:我的翻译层已经在我的应用程序中工作,并且还负责相关实体.它只是'实现一个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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我明白了为什么每次枚举查询时都会收到异常:实体框架的 IQueryable 基础结构的实现方式与构建 IQueryable 提供程序系列 pt. 中描述的模式非常不同。1 .
GetEnumerator()
该博客文章建议通过调用Execute()
提供商来实施。
相反,在 EF 基础结构中,ObjectQueryProvider 的Execute()
方法仅接受返回单个结果对象的表达式,而不接受返回结果对象的可枚举集合(这甚至记录在源代码中)。因此,ObjectQuery 的GetEnumerator()
方法不会调用,而是调用另一个直接从数据库获取结果的Execute()
方法。
因此,任何使用底层数据库查询来获取对象的转换 IQueryable 实现都必须使用相同的模式——转换GetEnumerator()
方法只是调用GetEnumerator()
底层数据库查询并将其注入到新的TranslatingEnumerator
.
归档时间: |
|
查看次数: |
8631 次 |
最近记录: |