`from..where` или` FirstOrDefault` в LINQ

Традиционно, когда я пытался получить данные для пользователя из базы данных, и я использовал следующий метод (в некоторой степени):

DbUsers curUser = context.DbUsers.FirstOrDefault(x => x.u_LoginName == id);
string name = curUser.u_Name;
string email = curUser.u_Email;

Вы можете видеть, что все, что я хочу сделать, это получить имя и электронную почту, но мне кажется, что этот запрос LINQ получает все, хранящиеся в базе данных этого пользователя, возвращая его, затем позволяет мне получить то, что я хочу.

Я занимаюсь некоторыми исследованиями и нашел следующую альтернативу:

var current = from s in context.DbUsers
where s.u_LoginName == id
select new {
             name = s.u_Name, 
             email = s.u_Email
           };
foreach (var user in current)
{
    //Stuff Here
}

Что было бы лучше, если вообще? Есть ли более легкий метод для использования, когда я хочу только получить несколько результатов/данных?

Ответ 1

Если вы хотите получить только два поля, тогда вы должны спроектировать свою сущность до того, как запрос будет выполнен (и в этом случае запрос будет выполнен, когда вы вызываете FirstOrDefault). Используйте оператор Select для проецирования на анонимный объект с обязательными полями:

var user = context.DbUsers
                  .Where(u => u.u_LoginName == id)
                  .Select(u => new { u.u_Name, u.u_Email })
                  .FirstOrDefault(); // query is executed here

string name = user.u_Name; // user is anonymous object
string email = user.u_Email;

Это будет генерировать SQL как:

 SELECT TOP 1 u_Name, u_Email FROM DbUsers
 WHERE u_LoginName = @id

Во втором случае вы выполняете проецирование до того, как запрос будет выполнен (т.е. начато перечисление). Поэтому загружаются только требуемые поля. Но запрос будет немного другим (без TOP 1). На самом деле, если вы преобразуете второй подход к синтаксису лямбда, он будет почти таким же:

var query = context.DbUsers
                   .Where(u => u.u_LoginName == id)
                   .Select(u => new { u.u_Name, u.u_Email }); 

// query is defined but not executed yet
foreach (var user in query) // executed now
{
   //Stuff Here
}

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

DbUsers user = context.DbUsers
                      .Where(u => u.u_LoginName == id)
                      .FirstOrDefault(); // query is executed here

string name = user.u_Name; // user is DbUsers entity with all fields mapped
string email = user.u_Email;    

В этом случае пользовательский объект не проецируется до выполнения запроса, и вы получите все поля пользователя, загруженные из базы данных и сопоставленные с пользовательским объектом:

 SELECT TOP 1 u_LoginName, u_Name, u_Email /* etc */ FROM DbUsers
 WHERE u_LoginName = @id

Ответ 2

Второй лучше. Вы получаете только необходимые данные из базы данных, чтобы сетевой трафик был легче.

Вы можете получить тот же результат с помощью методов расширения:

var user = context.DbUsers
                  .Where(x => x.u_LoginName == id)
                  .Select(x => new {...})
                  .FirstOrDefault();

Ответ 3

Если вам не нужна целая сущность, но некоторые значения из нее, используйте new {name = s.u_Name, email = s.u_Email}. Потому что этот объект намного "легче" для cunstruction. Когда вы получаете объект с FirstOrDefault, он "сохраняется в DBContext, но вы ничего не делаете с ним". Поэтому я советую вам получить только нужные вам данные.