简体   繁体   English

LINQ - EF Core - 返回 object 在嵌套列表中包含两个对象(由属性引用)

[英]LINQ - EF Core - Return object that contains two objects (referenced by property) in nested list

I know there are a million LINQ questions so I'm sorry for asking.我知道有一百万个 LINQ 问题,所以很抱歉问这个问题。 I've looked around, and I can't quite find the right answer.我环顾四周,我找不到正确的答案。 I suspect this is very easy but I'm new to LINQ and EF core.我怀疑这很容易,但我是 LINQ 和 EF 内核的新手。

I have the following List of type Room that is returned from a DB query (I only include one for brevity):我有以下从数据库查询返回的 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
            }
        ]
    }
]

Great.伟大的。 I want a LINQ query (preferably in fluent syntax, but I appreciate any help) that will query this list with many more Room's and return only one (I know there will only be one) that satisfies the following criterion:我想要一个 LINQ 查询(最好是流利的语法,但我很感激任何帮助),它将用更多房间查询这个列表并只返回一个(我知道只有一个)满足以下标准:

Only two users in the room, and both of the users are identified by userId's passed into the method.房间里只有两个用户,并且这两个用户都是通过传入方法的 userId 来标识的。

So if the method receives the following object:因此,如果方法接收到以下 object:

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

The room quoted above will be returned, and no other.上面引用的房间将被退回,其他房间不退还。

I hope this is clear.我希望这很清楚。

Thanks in advance!提前致谢!

Edit - fair enough, I include now a couple of the attempts that got me closer编辑 - 很公平,我现在包括一些让我更接近的尝试

I tried populating two users so that I could ask if room.Users.Contains() like so:我尝试填充两个用户,以便我可以询问 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);

However the comparison by User doesn't go through.然而,用户的比较并没有通过 go。 Is this just a problem of how I'm populating the User?这只是我如何填充用户的问题吗? Anyway, I know I shouldn't have to do that.无论如何,我知道我不应该那样做。

Then I tried what seems like the rigth approach in terms of direction, of just comparing the ID's like so:然后我尝试了在方向方面似乎是正确的方法,就像这样比较 ID:

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

But this returns null, yet I have confirmed that there is a room that satisfies the conditions I specify.但这会返回 null,但我已经确认有一个房间满足我指定的条件。

What gives me trouble is that EF core has generated a database schema that is unfamiliar to me.给我带来麻烦的是 EF 核心生成了一个我不熟悉的数据库模式。 In just SQL I would have tables ROOM, USER, ROOMUSER and inner join the ROOMUSER where both USER.id exist... and distinct, or something like that anyway.仅在 SQL 中,我将有表 ROOM、USER、ROOMUSER 和内部加入 ROOMUSER,其中 USER.id 都存在......并且不同,或者类似的东西。

Further edit:进一步编辑:

I include the models I am using to construct the DB below.我在下面包含了我用来构建数据库的模型。 Initially I did not because the JSON I included is the result of querying for all rooms and including their users (an ICollection within Room).最初我没有,因为我包含的 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; }
    }

So as I described how the tables would be if I had crafted it by hand, the idea is that User and Room are distinct entities (primary entities), and there would be a joining table to suggest their relationship.因此,正如我所描述的,如果我手工制作表格会是什么样子,这个想法是 User 和 Room 是不同的实体(主要实体),并且会有一个连接表格来暗示它们之间的关系。 Conceptually, a User is in a Room.从概念上讲,用户在房间中。

What might make things more clear as well is the initial JSON is returned by the following LINQ:以下 LINQ 返回的初始 JSON 也可能使事情更清楚:

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

Thus it should not be too difficult to ask for a room with the above conditions.因此,要求具有上述条件的房间应该不会太难。

Admittedly, as I said, I have not worked with EF core ever, and barely ever with LINQ.诚然,正如我所说,我从未使用过 EF 内核,也几乎没有使用过 LINQ。 I might be making a silly error...我可能犯了一个愚蠢的错误......

This one is close这个很近

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

but needs some adjustments但需要一些调整

  • And ( && ) operator is not appropriate - the id couldn't be equal to the both passed ids at the same time.并且 ( && ) 运算符不合适 - id 不能同时等于两个传递的 id。 It should be either one or another, ie what you need is Or ( || ) operator instead它应该是一个或另一个,即您需要的是 Or ( || ) 运算符

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

  • Any is not appropriate either. Any也不合适。 It would return a room having just one of the passed users, or both, but with additional users in there.它将返回一个房间,其中只有一个通过的用户,或两者兼有,但其中还有其他用户。 What you need is All instead.你需要的是All

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

However All also returns true for empty sets (ie when room.Users is empty), so it must be combined with another call to eliminate that case然而All也为空集返回 true(即当room.Users为空时),所以它必须与另一个调用结合以消除这种情况

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

which would produce the desired result, but is inefficient because is involving two subqueries instead of one.这将产生所需的结果,但效率低下,因为涉及两个子查询而不是一个。

Hence the best would be to use what I call "conditional counting" and compare to resulting count to two (2):因此,最好的办法是使用我所说的“条件计数”并将结果计数与两 (2) 进行比较:

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

which could also be expressed more generically (as it would support more than 2) with input like this也可以用这样的输入更通用地表达(因为它支持超过 2 个)

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

and the query condition like和查询条件如

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM