Рассмотрим, как в 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. Например, выберем из бд всех пользователей, которые работают в компании "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() можно задать условие запроса, которое транслируется в 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(). Данный метод не является методом 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
Но в качестве альтернативы мы можем использовать методы 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, которые извлекают соответственно любой единственный элемент и последний элемент последовательности.