给出以下linq-query:
var query1 = dbContext.MainTable.Where(m => m.MainId == _mainId).SelectMany(sub => sub.SubTable1)
.Select(sub1 => new
{
sub1.CategoryName,
VisibleDivisions = sub1.SubTable2
.Where(sub2 => sub2.Status == "Visible")
.Select(sub2 => new
{
/* select only what needed */
})
});
从我的主表开始,我希望将所有sub1与所有与sub1相关的sub2一起选中。 该查询按预期工作,生成一个将命中数据库的单个查询。
我的问题是关于内部Where
- 部分,因此此过滤器将用于应用程序中的其他几个部分。所以我想在一个地方定义这个“可见规则”(DRY原则)。
在期待Func<SubTable2, bool>
的地方,我写了以下属性
public static Expression<Func<SubTable2, bool>> VisibleOnlyExpression => sub2 => sub2.Status == "Visible";
并将我的查询更改为
var query1 = dbContext.MainTable.Where(m => m.MainId == _mainId).SelectMany(sub => sub.SubTable1)
.Select(sub1 => new
{
sub1.CategoryName,
VisibleDivisions = sub1.SubTable2
.Where(VisibleOnlyExpression.Compile())
.Select(sub2 => new
{
/* select only what needed */
})
});
这引发了一个异常,说明Internal .NET Framework Data Provider error 1025.
。
我已尝试使用相同的错误更改为.Where(VisibleOnlyExpression.Compile())
。
我知道这是因为EntityFramework
正在尝试将其转换为SQL
,而不是Where
。
我的问题是:如何在代码中的单个地点(DRY)定义我的“过滤规则”,但仍然可以在Select
- ,IQueryable
- 中使用,... -clauses可用于ICollection
以及var query = dbContext.MainTable
.Where(IsAwesome)
.SelectMany(s => s.SubTable1.Where(IsAlsoAwesome))
.Select(sub => new
{
Sub1sub2s = sub.SubTable2.Where(IsVisible),
Sub2Mains = sub.MainTable.Where(IsAwesome)
});
用于内部(子)查询?
我希望能够写出类似的内容:
IsAwesome
而IQueryable<MainTable>
- 规则首先在ICollection<MainTable>
上调用,以便仅获取真棒主条目,然后在子选择中的nptr
上仅获取与之相关的真棒主条目特定的SubTable2条目。但是规则 - 将MainTable条目定义为真棒 - 将无论我在何处调用/过滤它都将是相同的。
我想解决方案需要使用表达式树以及它们如何被操作,因此它们可以翻译成纯SQL但我没有得到正确的想法或指向开始。
答案 0 :(得分:1)
您可以使用LinqKit AsExpandable
和Invoke
这样的扩展方法,与您要求的内容接近:
var isAvesome = IsAwesome;
var isAlsoAwesome = IsAlsoAwesome;
var isVisible = IsVisible;
var query = dbContext.MainTable
.AsExpandable()
.Where(mt => isAwesome.Invoke(mt))
.SelectMany(s => s.SubTable1.Where(st1 => isAlsoAwesome.Invoke(st1)))
.Select(sub => new
{
Sub1sub2s = sub.SubTable2.Where(st2 => isVisible.Invoke(st2)),
Sub2Mains = sub.MainTable.Where(mt => isAwesome.Invoke(mt))
});
我说得很近,因为首先你需要将所有需要的表达式都拉到变量中,否则你将获得着名的EF&#34;方法不被支持&#34;例外。其次,调用并不像你的愿望那么简洁。但至少它允许你重用逻辑。
答案 1 :(得分:0)
AFAIK你想要做的事应该是完全可能的:
// You forgot to access ".Status" in your code.
// Also you don't have to use "=>" to initialize "IsVisible". Use the regular "=".
public static Expression<Func<SubTable2, bool>> IsVisible = sub2 =>
sub2.Status == "Visible";
...
VisibleDivisions = sub1
.SubTable2
// Don't call "Compile()" on your predicate expression. EF will do that.
.Where(IsVisibleOnly)
.Select(sub2 => new
{
/* select only what needed */
})
答案 2 :(得分:0)
我会准备如下的扩展方法:
public static IQueryable<SubTable2> VisibleOnly(this IQueryable<SubTable2> source)
{
return source.Where(s => s.Status == "Visible");
}
然后你可以这样使用它:
var query = dbContext.Table.VisibleOnly().Select(...)