C# 将谓词应用于联接表

C# 将谓词应用于联接表,c#,sql-server,linq,expression,C#,Sql Server,Linq,Expression,我试图在联接表上使用谓词,但我不知道如何表达它。一个例子可以最好地解释这个问题 我有两张桌子: User表(ID int,Name varchar(64),Email varchar(128),活动位) 事件表(ID int,Date-datetime(2),UserID int(外键),Description-varchar(max)) 一个用户可以有零到多个事件,并且一个事件仅与一个用户关联(通过外键) 现在我有一堆User谓词: Expression<Func<User,

我试图在联接表上使用谓词,但我不知道如何表达它。一个例子可以最好地解释这个问题

我有两张桌子:

  • User
    表(
    ID int
    Name varchar(64)
    Email varchar(128)
    活动位
  • 事件
    表(
    ID int
    Date-datetime(2)
    UserID int
    (外键),
    Description-varchar(max)
一个用户可以有零到多个事件,并且一个事件仅与一个用户关联(通过外键)

现在我有一堆
User
谓词:

Expression<Func<User, bool>> nameIsBob = u => u.Name == "Bob";
Expression<Func<User, bool>> isActive = u => u.Active;
但是,如果我从
事件列表开始,并且只想筛选属于某些用户的事件,我想执行类似操作,但我无法计算语法:

var query3 = ctx.Events
    .Where(e => e.User(nameIsBob))
    .Select(e => new { e.EventID, e.User.UserID });
问题是第二行谓词不能围绕
e.User
。我也试过:

var query4 = ctx.Events
    .Where(e => e.User.Where(nameIsBob))
    .Select(e => new { e.EventID, e.User.UserID });

关于如何获取所有属于名为Bob的人的事件,有什么建议吗?(可能有多个用户称为Bob)。我不能真正从用户表开始,因为上面的例子是我正在做的事情的简化版本(我希望我不是在描述一个!)

如果这不是数据库支持的,而是简单的客户端实体,你可以编译你的表达式,你可以像使用用户并返回bool的方法一样使用它:

Expression<Func<User, bool>> nameIsBob = u => u.Name == "Bob";

Func<User, bool> compiledNamedIsBob = nameIsBob.Compile();

var query3 = something.Events
    .Where(e => compiledNameIsBob(e.User))
您也可以对事件执行此操作,就像对用户执行此操作一样:

//like you did this
Expression<Func<User, bool>> nameIsBob = u => u.Name == "Bob";
Expression<Func<User, bool>> isActive = u => u.Active;

//you can do this
Expression<Func<Event, bool>> eventUserNameIsBob = e => e.User.Name == "Bob");
但是,如果您只希望使用
nameIsBob
表达式,而不希望使用
eventUserNameIsBob
表达式,则需要了解
nameIsBob
表达式仅适用于可查询的集合用户,因此您必须获取所有事件并从中挖掘用户,然后在挖掘出的用户上运行表达式,这意味着您基本上已经失去了对事件的了解(您必须通过询问用户来找回它):

这确实会像其他查询一样返回“一群用户”(
ctx.users.Where(nameIsBob)
),但除非您通过用户返回事件,否则您将丢失事件:

var query4 = ctx.Events.Select(e => e.User).Where(nameIsBob).Select(u => u.Event);
在这种情况下,你的DB会做什么,我不确定;您必须测试它/记录生成的SQL

就我个人而言,我会创建一个新的表达式来理解上面提到的事件(
eventUserNameIsBob
),或者我不会费心传递表达式,我只会内联定义它们:

var query3 = ctx.Events.Where(e => e.User.Name == "Bob"); //events owned by bobs
现代DB ORMs可以将其解压为
SELECT events.*FROM events在(…)上加入用户,其中users.name='bob'
,而不是像以前那样:

SELECT id FROM users WHERE name = 'bob' --returns list of 1,2,4,5
SELECT * FROM events WHERE userid = 1
SELECT * FROM events WHERE userid = 2
SELECT * FROM events WHERE userid = 4
SELECT * FROM events WHERE userid = 5

太好了。谢谢你对表达式和函数的解释。预定义my表达式的基本原理是,该表达式实际上是基于外部因素从调用方传入的。您给出的建议非常有效,谢谢。是的,我希望查询将作为(内部)联接编译为SQL,而不是选择用户ID列表,然后对
UserID=
中的
UserID的事件执行第二次(或多次)查询。谢谢。好的,我想这会解决我的问题,但这会引发异常“在Linq to Entities中不支持调用”。运行时:-(好吧,我做了一个编辑,希望能为这个上下文提供一些更好的建议(双关语不是故意的!)好吧,也不要低估SQL优化器的功能;您可能会发现
SELECT事件where UserID IN(SELECT…bob)
的计划和执行方式与
选择事件加入用户的方式相同。其中bob
-查看各种选项的查询计划
var query4 = ctx.Events.Select(e => e.User).Where(nameIsBob);
var query4 = ctx.Events.Select(e => e.User).Where(nameIsBob).Select(u => u.Event);
var query3 = ctx.Events.Where(e => e.User.Name == "Bob"); //events owned by bobs
SELECT id FROM users WHERE name = 'bob' --returns list of 1,2,4,5
SELECT * FROM events WHERE userid = 1
SELECT * FROM events WHERE userid = 2
SELECT * FROM events WHERE userid = 4
SELECT * FROM events WHERE userid = 5