如何拦截IQueryProvider查询的结果(单个结果除外)

时间:2012-01-05 20:30:33

标签: linq entity-framework

我正在使用Entity Framework,我有一个custum IQueryProvider。我使用Execute方法,以便我可以在执行后修改查询的结果(POCO)。我想对收藏品做同样的事情。问题是只对单个结果调用Execute方法。

如MSDN所述:

  

Execute方法执行返回单个值的查询   (而不是可枚举的值序列)。表达树那个   表示返回可枚举结果的查询   它们的相关IQueryable对象被枚举。

还有另一种方法可以实现我想要的错过吗?

我知道我可以在存储库中编写一个特定的方法,或者我想将其应用于所有可能的查询。

3 个答案:

答案 0 :(得分:1)

确实,实际的签名是:

public object Execute(Expression expression)
public TResult Execute<TResult>(Expression expression)

然而,并不意味着 TResult永远是一个元素!它是期望从表达式返回的类型。

另请注意,TResult上有无约束,甚至不是'class'或'new()'。

如果您的表达式具有单一结果,则TResult为MyObject,例如.FirstOrDefault()。但是,当您double超过查询时,TResult也可以是.Avg(),当您的查询为普通IEnumerable<MyObject>时,它也可以是.Select.Where

证明(*) - 我刚刚在我的Execute()实现中设置了一个断点,并且我用Watches检查了它:

typeof(TResult).FullName    "System.Collections.Generic.IEnumerable`1[[xxxxxx,xxxxx]]"
expression.Type.FullName    "System.Linq.IQueryable`1[[xxxxxx,xxxxx]]"

我承认三个重载,一个object,一个TResult和一个IEnumerable<TResult>可能更具可读性。我认为他们没有将其中三个作为未来接口的可扩展性点。我可以想象,将来他们会提出比IEnumerable更强大的东西,然后他们需要添加另一个重载等等。简单地说,这个界面可以处理任何类型

哦,看,我们现在除了IQueryable之外还有IEnumerable,所以它至少需要四次重载:)

Proof标有(*),因为我的IQueryProvider代码中有一个小错误/特征,它掩盖了LINQ的真实行为。

LINQ确实仅针对奇异案例调用通用Execute。这是一种快捷方式,优化

对于所有其他情况,它...... 根本不会调用Execute()

对于所有其他情况,LINQ在您的自定义.GetEnumerator实现上调用IQueryable<>,所发生的事情由......在您那里写的内容决定。我的意思是,假设您实际提供了IQueryable的自定义实现。如果你不这样做会很奇怪 - 总共只有15行,与自定义提供商的长度相比没什么。

在我得到“证据”的项目中,我的实现如下:

public System.Collections.IEnumerator GetEnumerator()
{
    return Provider.Execute<IEnumerable>( this.Expression ).GetEnumerator();
}

public IEnumerator<TOut> GetEnumerator()
{
    return Provider.Execute<IEnumerable<TOut>>( this.Expression ).GetEnumerator();
}

当然,由于名称冲突,其中一个是明确的。请注意,为了获取枚举器,我实际上用明确说明的TResult来调用Execute。这就是为什么我的“证据”中出现了这些类型。

我认为你看到了“TResult = Single Element”的情况,因为你写的是这样的东西:

public IEnumerator<TOut> GetEnumerator()
{
    return Provider.Execute<TOut>( this.Expression ).GetEnumerator();
}

这真的无法选择您的Execute实现,并且必须返回单个元素。恕我直言,这只是你的代码中的一个错误。你可以像我上面的例子那样做,或者你可以简单地使用无类型的执行:

public System.Collections.IEnumerator GetEnumerator()
{
    return ((IEnumerable)Provider.Execute( this.Expression )).GetEnumerator();
}

public IEnumerator<TOut> GetEnumerator()
{
    return ((IEnumerable<TOut>)Provider.Execute( this.Expression )).GetEnumerator();
}

当然,您的Execute实现必须确保为此类查询返回正确的IEnumebles!

答案 1 :(得分:0)

  

表示返回可枚举结果的查询的表达式树在枚举其关联的IQueryable对象时执行。

我建议枚举您的查询:

foreach(T t in query)
{
  CustomModification(t);
}

您的IQueryProvider必须实施CreateQuery<T>。您可以选择生成的IQueryable的实现。如果您希望IQueryable在枚举时对每一行执行某些操作,则可以编写该实现。

答案 2 :(得分:-1)

最后的答案是,这是不可能的。