【问题标题】:LINQ - EF Core - Return object that contains two objects (referenced by property) in nested listLINQ - EF Core - 在嵌套列表中返回包含两个对象(由属性引用)的对象
【发布时间】:2021-07-26 08:18:05
【问题描述】:

我知道有一百万个 LINQ 问题,所以很抱歉问这个问题。我环顾四周,我找不到正确的答案。我怀疑这很容易,但我是 LINQ 和 EF 核心的新手。

我有以下从数据库查询返回的 Room 类型列表(为简洁起见,我只包括一个):

[
    {
        "roomId": 2,
        "roomName": "test",
        "users": [
            {
                "userId": 2,
                "username": "seconduser",
                "password": "demo",
                "token": null,
                "refreshToken": null,
                "alias": "test",
                "friends": null
            },
            {
                "userId": 3,
                "username": "thirduser",
                "password": "demo",
                "token": null,
                "refreshToken": null,
                "alias": "test",
                "friends": null
            }
        ]
    }
]

太好了。我想要一个 LINQ 查询(最好是流利的语法,但我感谢任何帮助),它将用更多 Room 查询此列表并仅返回一个满足以下标准的(我知道只有一个):

房间里只有两个用户,两个用户都是通过传入方法的userId来标识的。

所以如果方法接收到以下对象:

{
    "friendId": 1,
    "requestUserId": 3
}

上面引用的房间将被退回,其他的都不会。

我希望这很清楚。

提前致谢!

编辑 - 很公平,我现在包括一些让我更接近的尝试

我尝试填充两个用户,以便我可以询问 room.Users.Contains() 是否像这样:

var requestingUser = UserService.GetSingleUser(request.RequestUserId);
            
var friendUser = UserService.GetSingleUser(srequest.FriendId);

var test = allRooms.FirstOrDefault(room => room.Users.Count == 2 && room.Users.Contains(friendUser) && room.Users.Contains(requestingUser);

但是用户的比较没有通过。这只是我如何填充用户的问题吗?无论如何,我知道我不应该这样做。

然后我尝试了在方向方面看似正确的方法,只是像这样比较 ID:

var test = allRooms.First(room => room.Users.Any(user =>
    user.userId == request.FriendId && user.userId == request.RequestUserId));

但这返回null,但我已经确认有一个房间满足我指定的条件。

让我感到麻烦的是,EF 核心生成了一个我不熟悉的数据库模式。仅在 SQL 中,我将有表 ROOM、USER、ROOMUSER 和内部加入 ROOMUSER,其中 USER.id 都存在......并且不同,或者类似的东西。

进一步编辑:

我在下面包含了我用来构建数据库的模型。最初我没有这样做,因为我包含的 JSON 是查询所有房间并包括其用户的结果(房间内的 ICollection)。

    public class User
    {
        public int UserId { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string Token { get; set; }
        public string RefreshToken { get; set; }
        public string Alias { get; set; }
        
        public ICollection<User> Friends { get; set; }
    }

    public class Room
    {
        public int RoomId { get; set; }
        public string RoomName { get; set; }
        
        public ICollection<User> Users { get; set; }
    }

因此,正如我所描述的,如果我手工制作表格会是什么样子,其想法是 User 和 Room 是不同的实体(主要实体),并且会有一个连接表格来暗示它们之间的关系。从概念上讲,用户在房间中。

下面的 LINQ 返回初始 JSON:

var allRooms = db.Rooms.Include(x => x.Users).ToList();

因此,要求具有上述条件的房间应该不会太难。

诚然,正如我所说,我从未使用过 EF 内核,也几乎没有使用过 LINQ。我可能犯了一个愚蠢的错误......

【问题讨论】:

  • @Silvermind 够公平的,你说得对。问题已更新。
  • 什么是room.Users?显示一些数据是可以的,但更重要的是显示查询中使用的 model - 即类及其属性(尤其是 navigation properties)。因为您所解释的幕后仍然有 3 个表,但是在 EFC 5.0+ 中(是您正在使用的版本吗?)多对多关系可以在对象模型中以 2 种不同的方式表示,并且影响您查询它的方式。查看代码,显然room.Users 是一个集合,但是是什么?换句话说,user 是什么room.Users.Any(user =&gt; 包括那个类。
  • @IvanStoev 更新了更多信息。因此,room.Users 正在访问 Room 对象中的 User 集合。上面的教室应该更清楚。
  • 嗯,这不是我预期的多对多,而是与User 绑定到Room 的一对多。有点奇怪,但无论如何,这实际上不应该影响查询。

标签: c# linq entity-framework-core


【解决方案1】:

这个很接近

room => room.Users.Any(user =>
    user.userId == request.FriendId && user.userId == request.RequestUserId)

但需要一些调整

  • And (&amp;&amp;) 运算符不合适 - id 不能同时等于两个传递的 id。它应该是一个或另一个,即您需要的是 Or (||) 运算符

    user.userId == request.FriendId || user.userId == request.RequestUserId

  • Any 也不合适。它将返回一个房间,其中只有一个通过的用户,或两者兼有,但其中还有其他用户。你需要的是All

    room.Users.All(用户 => user.userId == request.FriendId || user.userId == request.RequestUserId)

不过,All 也为空集返回 true(即当 room.Users 为空时),因此必须结合另一个调用来消除这种情况

room.Users.Any() && room.Users.All(user =>
    user.userId == request.FriendId || user.userId == request.RequestUserId)

这将产生所需的结果,但效率低下,因为它涉及两个子查询而不是一个。

因此,最好的方法是使用我所说的“条件计数”并将结果计数与两 (2) 进行比较:

room.Users.Count(user =>
    user.userId == request.FriendId || user.userId == request.RequestUserId) == 2

也可以用这样的输入更通用地表达(因为它支持超过 2 个)

// could contain more than two ids and doesn't need to be array
var userIds = new [] { request.FriendId, request.RequestUserId }.AsEnumerable();

查询条件如

room.Users.Count(user => userIds.Contains(user.userId)) == userIds.Count()

【讨论】:

  • 谢谢。所以最后它主要是 && 而不是 || 的简单逻辑错误。很明显,LINQ 非常强大,但需要一些时间来适应。我正在研究一些示例并使用 LinqPad。我很欣赏 Any() 的解释和 Count() 的使用。不仅如此,我也很清楚我也不太了解 EF Core。我只使用过原始数据库提供程序和 SQL。我现在正在通过 EF Core In Action 工作。似乎您真的可以根据需要对数据库进行建模,只需通过代码而不是 SQL。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-26
相关资源
最近更新 更多