Запрос LINQ с внутренним соединением не работает при добавлении нескольких записей в базу данных

#c# #entity-framework #linq #linq-to-sql

#c# #сущность-фреймворк #linq #linq-to-sql

Вопрос:

У меня есть функция, которая требует возврата всех пользователей, за исключением тех, которые были удалены, я написал следующий запрос

 var userLists = await _repositoryContext.User
             Join(_repositoryContext.Eliminations,
             u => currentUser.userID,
             m => m.userOneId,
             (u, m) => new { Eliminations = m, Users = u })
            .Where(u_m => u_m.Users.userID != currentUser.userId amp;amp; u_m.Eliminations.userTwoId != u_m.Users.userID).Select(u_m => u_m.Users).ToListAsync();
 

Вот как будет выглядеть таблица пользователя

  userID: cf321a6a-70de-45c9-997d-de7d2be99a36
 name: test1
 email: test1@gmail.com
-------------------------------------------------------------
 userID: b1b2f69a-2100-499b-a0e6-42bbf87290ee
 name: test2
 email: test2@gmail.com
-------------------------------------------------------------
 userID: b2be45d2-f065-4321-842d-cc35d29c374f
 name: test3
 email: test3@gmail.com
-------------------------------------------------------------
 

Этот запрос отлично работает, когда таблица исключений выглядит следующим образом

  userMatchID: 7b2bc383-a818-4662-8f62-4f407650da9e
 userOneWin: 1
 userTwoWin: 0
 userOneId: cf321a6a-70de-45c9-997d-de7d2be99a36
 userTwoId: b2be45d2-f065-4321-842d-cc35d29c374f
 

Когда test3 user находится в таблице исключений, запрос работает нормально и возвращает test2 user

  userID: b1b2f69a-2100-499b-a0e6-42bbf87290ee
 name: test2
 email: test2@gmail.com
 

Но когда test2 user добавляется в таблицу исключений, как показано ниже

  userMatchID: 7b2bc383-a818-4662-8f62-4f407650da9e
 userOneWin: 1
 userTwoWin: 0
 userOneId: cf321a6a-70de-45c9-997d-de7d2be99a36
 userTwoId: b2be45d2-f065-4321-842d-cc35d29c374f
--------------------------------------------------------------
 userMatchID: b9be0fd3-7e4f-4311-901e-c28715ee13d1
 userOneWin: 1
 userTwoWin: 0
 userOneId: cf321a6a-70de-45c9-997d-de7d2be99a36
 userTwoId: b1b2f69a-2100-499b-a0e6-42bbf87290ee
 

запрос завершается ошибкой и отправляет обоих доступных в данный момент пользователей вместо отправки пустого списка.Который выглядит следующим образом

 -------------------------------------------------------------
 userID: b1b2f69a-2100-499b-a0e6-42bbf87290ee
 name: test2
 email: test2@gmail.com
-------------------------------------------------------------
 userID: b2be45d2-f065-4321-842d-cc35d29c374f
 name: test3
 email: test3@gmail.com
-------------------------------------------------------------
 

Но результат, который я хочу, — это пустой список без записей, test1 user который в любом случае опущен в запросе Linq. Я что-то делаю не так?

Комментарии:

1. Неясно, что вы возвращаете и чего ожидаете. Из того, что я прочитал о том, как вы структурировали запрос с помощью объединения, похоже, что вы вернете две ссылки на идентификатор пользователя: cf321a6a-70de-45c9-997d-de7d2be99a36, что, вероятно, не то, что вы ожидаете.

2. @StevePy Я обновил вопрос с результатами на каждом этапе и ожидаемым результатом

Ответ №1:

Я сомневаюсь, что это сработает, потому что это не так, как вы сделали бы это в обычной базе данных. Если у вас было два списка пользователей, вы не можете ответить «каких из этих пользователей нет в этом списке» исключительно внутренним соединением — объединяя их всех вместе и запрашивая работу this.userid != that.userid wil, когда в «этом» списке есть только один пользователь, но как только вы добавите два, вы получитевсе пользователи из «этого» списка, потому что каждый из них не равен хотя бы одному из пользователей из этого списка

 This
A
B

That
A
 

Возвращается только B, потому что B != A, (и A == A)

 This
A
B
C

That
A
B
 

A возвращается, потому что A != B, B возвращается, потому что B != A, C возвращается дважды, потому что C != A и C != B

Короче говоря, не используйте для этого inner join. Посмотрите на шаблон, больше похожий

 var elim1 = DbContext.Eliminations.Where(e => !e.UserOneWin).Select(e => e.UserOneId);
var elim2 = DbContext.Eliminations.Where(e => !e.UserTwoWin).Select(e => e.UserTwoId);
DbContext.Users.Where(u => !elim1.Any(e => e.UserOneId == u.UserId) amp;amp; !elim2.Any(e => e.UserTwoId == u.UserId))
 

Это должно быть сопоставлено с sql, более похожим на

 Users u
WHERE 
  NOT EXISTS (SELECT null FROM eliminations WHERE u.UserId = userOneId) AND 
  NOT EXISTS (SELECT null FROM eliminations WHERE u.UserId = userTwoId) 
 

EXISTS лучше подходит для ответа на этот вопрос, чем внутреннее соединение. В качестве альтернативы вы могли бы внутренне присоединить пользователей к отдельным победителям, а не к проигравшим. Честно говоря, я не думаю, что ваш дизайн стола приносит вам здесь какую-то пользу. У меня были бы исключения, сопоставляющие winninguserid и losinguserid, а не user one и user two и еще один столбец, чтобы сказать, выиграл ли пользователь, и еще один столбец, чтобы сказать, проиграл ли пользователь. На данный момент каждый раз, когда вам нужен «список победителей» или «список проигравших», вы должны дважды попасть в таблицу отсева, эффективно объединяя всех пользователей, где winner1 имеет значение true, в userTwo, где winner2 имеет значение true, что намного неудобнее, чем просто иметь идентификатор победителя и идентификатор проигравшего колонка

Комментарии:

1. Я понял, что это может быть сложно сделать, когда записи продолжают накапливаться, поэтому на данный момент я решил добавить функцию в кодовую базу для сравнения и удаления пользователей из списка