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

Последнее обновление: 21.11.2021

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

using Microsoft.EntityFrameworkCore;

public class Company
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public List<User> Users { get; set; } = new();
}

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<User> Users { get; set; } = null!; 
    public DbSet<Company> Companies { get; set; } = null!;

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=helloapp.db");
    }
}

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

С помощью метода EF.Functions.Like() можно задать условие запроса, которое транслируется в Entity Framework Core в выражение с оператором LIKE. Метод принимает два параметра - оцениваемое выражение и шаблон, с которым сравнивается его значение. Например, найдем всех пользователей, в имени которых присутствует подстрока "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})");
}

Выражение EF.Functions.Like(p.Name!, "%Tom%")) означает, что мы ищем строки, где в свойстве Name содержиться подстрока "Tom". Поскольку Name представляет nullable-тип, то после названия свойства указывается оператор !.

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

SELECT "u"."Id", "u"."Age", "u"."CompanyId", "u"."Name"
      FROM "Users" AS "u"
      WHERE "u"."Name" LIKE '%Tom%'

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

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

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

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

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

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

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

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

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

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

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

Find/FindAsync

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

User? user = db.Users.Find(3); // выберем элемент с id=3
// асинхронная версия
// User? user = await db.Users.FindAsync(3); // выберем элемент с id=3
if(user!=null) Console.WriteLine($"{user.Name} ({user.Age})");

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

SELECT "u"."Id", "u"."Age", "u"."CompanyId", "u"."Name"
FROM "Users" AS "u"
WHERE "u"."Id" = @__p_0
LIMIT 1

First/FirstOrDefault/FirstAsync/FirstOrDefaultAsync

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

С помощью данных методов можно просто получить первый объект из выборки:

User? user = db.Users.FirstOrDefault();
// асинхронная версия
// User? user = await db.Users.FirstOrDefaultAsync();
if(user!=null) Console.WriteLine(user.Name);

Но в качестве параметра также можно передать условие. Тогда в выборку попадают только те объекты, которые соответствуют условию:

User? user = db.Users.FirstOrDefault(p=>p.Id==3);
// асинхронная версия
// User? user = await db.Users.FirstOrDefaultAsync(p=>p.Id==3);
if(user!=null) Console.WriteLine(user.Name);

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

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850