【问题标题】:How can I intercept the result of an IQueryProvider query (other than single result)如何拦截 IQueryProvider 查询的结果(除了单个结果)
【发布时间】:2012-01-05 20:30:33
【问题描述】:

我正在使用实体框架,并且我有一个自定义 IQueryProvider。我使用 Execute 方法,以便在执行后修改查询的结果(POCO)。我想对收藏做同样的事情。问题是 Execute 方法只针对单个结果调用。

如 MSDN 上所述:

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

有没有其他方法可以完成我想要但我错过的事情?

我知道我可以在存储库或其他任何内容中编写特定方法,但我想将其应用于所有可能的查询。

【问题讨论】:

    标签: linq entity-framework


    【解决方案1】:

    这是真的,实际的签名是:

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

    但是,这并不意味着 TResult 将始终是单个元素!它是预期从表达式返回的类型。

    另外,请注意,TResult 没有没有限制,甚至没有 'class' 或 'new()'。

    当您的表达式是单数结果时,TResult 是 MyObject,例如 .FirstOrDefault()。但是,当您对查询进行.Avg() 时,TResult 也可以是double,当您的查询是普通的.Select.Where 时,它也可以是IEnumerable&lt;MyObject&gt;

    Proof(*) - 我刚刚在我的 Execute() 实现中设置了一个断点,并使用 Watches 对其进行了检查:

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

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

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

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

    LINQ 确实只为个别情况调用泛型 Execute。这是一个捷径,一个优化

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

    对于所有其他情况,LINQ 在您的自定义 IQueryable&lt;&gt; 实现上调用 .GetEnumerator,所发生的事情是由 .. 决定的,只是您在那里写的内容。我的意思是,假设您实际上提供了 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 实现无需选择,并且必须返回单个元素。恕我直言,这只是您代码中的一个错误。你可以像我上面的例子那样做,或者你可以简单地使用无类型的 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 实现必须确保为此类查询返回正确的 IEnumerables!

    【讨论】:

      【解决方案2】:

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

      我建议列举您的查询:

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

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

      【讨论】:

      • 是的,如果它是一个查询,这将起作用。但是我正在构建一个框架,我没有一个可以枚举查询的点。我想坐在实体框架之上进行每个查询。正如我所说,我想要 IQueryProvider 的 Execute 方法的完全等价物,但要获得枚举结果。
      【解决方案3】:

      最后的答案是不可能的。

      【讨论】:

        猜你喜欢
        • 2013-02-07
        • 2013-12-29
        • 2015-03-16
        • 1970-01-01
        • 1970-01-01
        • 2022-11-30
        • 2011-12-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多