2013-08-29 69 views
2

我让代码自己说话:IQueryable扩展方法的流利语法?

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到实体无法识别方法“System.Collections.Generic.IQueryable`1 [事物] ActiveThing'方法,并且此方法不能转换为商店表达式。

你的想法:我想一口流利的一种表达这个的,这样对任何ISoftDeletable我可以用一个简单的,可重用的代码上“其中”条款补充方式。这里的例子不起作用,因为Linq2Entities不知道如何处理我的Active()方法。

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

有什么建议吗?

+3

你还没有说*你得到的例外是什么,这使得它很难帮助你。我希望你迄今所做的一切都很好 - 但更复杂的例子不会,例如在连接中使用'Active'。 –

+0

@JonSkeet - 添加错误消息。但是,是的,你是对的,我的实际使用确实涉及到一个加入。 –

+0

并且您是否使用简单的代码复制了该错误?我怀疑没有 - 如果有代码*会重现错误,这将会很有用。 –

回答

2

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

此问题的解决方案是为您的扩展方法添加class限制。如果不添加此限制,则表达式编译到包括投:以上

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

演员混淆了实体框架,所以这就是为什么你会得到一个运行时错误。

当添加限制,表达成为一个简单的属性访问:

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

该表达式可以被解析只是实体框架细。

的第二个问题是,你可以不适用在查询语法用户定义的扩展方法,但是你可以用它们在流利的语法:

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 { ... }); 

活性()调用,但作为EF解析表达式生成。果然,EF不知道如何处理这样的功能,因此它可以解决问题。

一个明显的解决方法(但并不总是可能的)是在Thing S的替代ThingContainers开始查询:

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

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

+0

试过这个,它没有帮助...同样的错误 –

+0

它确实帮助我的一个相关点,但是,所以+1,谢谢! –

+0

@Shaul ,我已经扩展了答案。基本上,混合查询语法和扩展方法是一件很痛苦的事情。 – felipe

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()条件。