2

我会让代码自己说话:

public interface ISoftDeletable {
  bool IsDeleted {get; set;}
}

public static class Extensions {
  public IQueryable<T> Active<T>(this IQueryable<T> q) where T : ISoftDeletable {
    return q.Where(t => !t.IsDeleted);
  }
}

public partial class Thing : ISoftDeletable {
  ...
}

...
var query = from tc in db.ThingContainers
            where tc.Things.Active().Any(t => t.SatisfiesOtherCondition)
            select new { ... }; // throws System.NotSupportedException

错误是:

LINQ to Entities 无法识别方法 'System.Collections.Generic.IQueryable`1[Thing] ActiveThing' 方法,并且此方法无法转换为存储表达式。

你明白了:我想要一种流利的表达方式,这样ISoftDeletable我就可以用一段简单的、可重用的代码添加一个“where”子句。此处的示例不起作用,因为 Linq2Entities 不知道如何处理我的Active()方法。

我在这里给出的例子很简单,但在我的真实代码中,Active()扩展包含一组更复杂的条件,我不想在我的代码中复制和粘贴。

有什么建议么?

4

2 回答 2

2

您在代码中有两个不相关的问题。第一个是实体框架无法处理表达式中的强制转换,而您的扩展方法可以。

解决此问题的方法是在class您的扩展方法中添加限制。如果不添加该限制,则编译表达式以包含强制转换:

.Where (t => !((ISoftDeletable)t.IsDeleted))

上面的演员混淆了实体框架,所以这就是你得到运行时错误的原因。

添加限制后,表达式变为简单的属性访问:

 .Where (t => !(t.IsDeleted))

这个表达式可以用实体框架很好地解析。

第二个问题是您不能在查询语法中应用用户定义的扩展方法,但您可以在 Fluent 语法中使用它们:

db.ThingContainers.SelectMany(tc => tc.Things).Active()
    .Any(t => t.SatisfiesOtherCondition); // this works

要查看问题,我们必须查看实际生成的查询是什么:

 db.ThingContainers
       .Where(tc => tc.Things.Active().Any(t => t.StatisfiesOtherCondition))
       .Select(tc => new { ... });

Active() 调用永远不会执行,而是生成为表达式供 EF 解析。果然,EF 不知道如何处理这样的函数,所以它退出了。

Thing一个明显的解决方法(尽管并非总是可能)是在s 而不是 s处开始查询ThingContainers

db.Things.Active().SelectMany(t => t.Container);

另一种可能的解决方法是使用模型定义的函数,但这是一个更复杂的过程。有关更多信息,请参阅MSDN 文章。

于 2013-08-29T17:13:27.830 回答
0

虽然@felipe 获得了答案,但我想我也会发布自己的答案作为替代方案,尽管它是类似的:

var query = from tc in db.ThingContainers.Active() // ThingContainer is also ISoftDeletable!
            join t in db.Things.Active() on tc.ID equals t.ThingContainerID into things
            where things.Any(t => t.SatisfiesOtherCondition)
            select new { ... };

这具有保持查询结构或多或少相同的优点,尽管您确实失去了和之间隐式关系的流畅ThingContainerThing。在我的情况下,交易结果表明,最好明确指定关系,而不是明确指定Active()标准。

于 2013-08-29T20:50:46.913 回答