IQueryable扩展方法的流畅语法?

时间:2013-08-29 16:37:48

标签: c# linq linq-to-entities entity-framework-5

我会让代码说明一切:

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()扩展包含更复杂的条件,我不想复制和粘贴我的所有条件。代码。

有什么建议吗?

2 个答案:

答案 0 :(得分:2)

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

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

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

上面的强制转换会混淆Entity Framework,因此这就是您遇到运行时错误的原因。

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

 .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而不是ThingContainers开始查询:

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

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

答案 1 :(得分: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()标准。