Выборка и фильтрация

  • Михаил
  • 12 мин. на прочтение
  • 136
  • 15 Nov 2022
  • 15 Nov 2022

Рассмотрим, как в EF Core выполнять фильтрацию. Для этого используем модели из прошлой темы:

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<User> Users { get; set; } = new List<User>();
}
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int CompanyId { get; set; }
    public Company Company { get; set; }
}
public class ApplicationContext : DbContext
{
    public DbSet<Company> Companies { get; set; }
    public DbSet<User> Users { get; set; }
         
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;");
    }
}

Where

Если необходимо отфильтровать получаемые данные, то для этого можно использовать метод Where. Например, выберем из бд всех пользователей, которые работают в компании "Google":

using(ApplicationContext db = new ApplicationContext())
{
    var users = db.Users.Where(p=> p.Company.Name=="Google");
    foreach (User user in users)
        Console.WriteLine($"{user.Name} ({user.Age})");
}

Аналогичный запрос с помощью операторов LINQ:

using(ApplicationContext db = new ApplicationContext())
{
     var users = (from user in db.Users
                   where user.Company.Name == "Google"
                   select user).ToList();
    foreach (User user in users)
        Console.WriteLine($"{user.Name} ({user.Age})");
}

EF.Functions.Like

Начиная с версии 2.0 в Entity Framework Core можно использовать метод EF.Functions.Like(). Он позволяет транслировать условие в выражение с оператором LIKE на стороне MS SQL Server. Метод принимает два параметра - оцениваемое выражение и шаблон, с которым сравнивается его значение. Например, найдем всех пользователей, в имени которых присутствует подстрока "Tom" (это могут быть "Tom", "Tomas", "Tomek", "Smith Tom"):

using (ApplicationContext db = new ApplicationContext())
{
    var users = db.Users.Where(p => EF.Functions.Like(p.Name, "%Tom%"));
    foreach (User user in users)
        Console.WriteLine($"{user.Name} ({user.Age})");
}

На стороне БД этот запрос будет транслироваться в следующую SQL-команду:

SELECT [p].[Id], [p].[CompanyId], [p].[Name], [p].[Age]
            FROM [Users] AS [p]
            WHERE [p].[Name] LIKE N'%Tom%'

Для определения шаблона могут применяться ряд специальных символов подстановки:

%: соответствует любой подстроке, которая может иметь любое количество символов, при этом подстрока может и не содержать ни одного символа

_: соответствует любому одиночному символу

[ ]: соответствует одному символу, который указан в квадратных скобках

[ - ]: соответствует одному символу из определенного диапазона

[ ^ ]: соответствует одному символу, который не указан после символа ^

Стоит отметить, что в качестве первого параметра метод принимает оцениваемое выражение в виде строки. В случае со свойством Name все просто, так как оно представляет тип string. Но если нам необходимо использовать в качестве оцеениваемого выражения другие свойства, то их следует привести к строке. Например, найдем всех пользователей у которых возраст (свойство Age) в диапазоне от 22 до 29:

Например, следующее выражение:

var users = db.Users.Where(u => EF.Functions.Like(u.Age.ToString(), "2[2-9]"));

Подобным образом метод EF.Functions.Like() можно использовать с операторами LINQ:

var users = from u in db.Users
             where EF.Functions.Like(u.Age.ToString(), "2[2-9]")
             select u;

Find

Для выборки одного объекта мы можем использовать метод Find(). Данный метод не является методом Linq, он определен у класса DbSet:

User user = db.Users.Find(3); // выберем элемент с id=3

При выполнении запроса он будет трансформироваться в следующее выражение SQL:

SELECT TOP(1) [e].[Id], [e].[CompanyId], [e].[Name], [e].[Age]
FROM [Users] AS [e]
WHERE [e].[Id] = 3

First/FirstOrDefault

Но в качестве альтернативы мы можем использовать методы Linq First()/FirstOrDefault(). Они получают первый элемент выборки, который соответствует определенному условию или набору условий. Использование метода FirstOrDefault() является более гибким, так как если выборка пуста, то он вернет значение null. А метод First() в той же ситуации выбросит ошибку.

User user = db.Users.FirstOrDefault(p=>p.Id==3);
if(user!=null)
    Console.WriteLine(user.Name);

По тому же принципу работают пары методов Single/SingleOrDefault и Last/LastOrDefault, которые извлекают соответственно любой единственный элемент и последний элемент последовательности.