Бекенд на C# Web Api
Запросы к базе данных
База данных внедряется в конструктор с помощью механизма внедрения зависимостей. Обычно базой данных пользуются в сервисах.
public class AuthService
{
// база данных внедряется в конструктор
public AuthService(MyDbContext database)
{ ... }
}Например, сервис аутентификации может обращаться к БД чтобы проверять логин и пароль.
Запросы к базе данных обычно пишутся с использованием языка SQL. Можно использовать ADO.NET - низкоуровневый подход для работы с базой данных. Его преимуществом является полный контроль над SQL запросами.
Однако мы будем использовать более высокоуровневый подход и писать запросы с помощью Entity Framework. Таким образом всеми запросами можно управлять с помощью обычных C# методов, лямбда выражений или с помощью языка LINQ.
В примере ниже происходит добавление, удаление и обновление записей в таблице PlayerProfiles. Обратите внимание, что в данном случае по возможности используются асинхронные методы для работы с базой данных.
public async Task ExampleMethod()
{
await _dbContext.PlayerProfiles.AddAsync(playerProfile1);
await _dbContext.PlayerProfiles.AddAsync(playerProfile1);
_dbContext.PlayerProfiles.Remove(oldPlayerProfile);
_dbContext.PlayerProfiles.Update(updatePlayerProfile);
await _dbContext.SaveChangesAsync();
}Для выборки данных можно воспользоваться методом Select или запросом с помощью LINQ.
public async Task ExampleMethod()
{
// выборка всех профилей, в которых указан мужской пол
_dbContext.PlayerProfiles.Select(profile => profile.gender == Gender.Male);
// выборка всех карт с LEFT JOIN по таблице загрузок
// для подсчета числа скачиваний
var result = await (from map in _dbContext.CustomMaps
join download in _dbContext.Downloads
on map.map_id equals download.map_id into downloadsGroup
from download in downloadsGroup.DefaultIfEmpty()
group download by map into grouped
select new MapInfoDto
{
map_id = grouped.Key.map_id,
name = grouped.Key.name,
description = grouped.Key.description,
player_id = grouped.Key.player_id,
downloads = grouped.Count(d => d != null),
}).ToArrayAsync();
}Загрузка связей при обращении к внешним ключам
Явная загрузка — наиболее эффективный и предпочитаемый способ загружать данные, связанные через внешние ключи, поскольку он позволяет сразу подгружать связанные данные с использованием одного SQL-запроса.
Пример: предположим я загружаю данные subscriptions и хочу вместе с ними сразу же получить связанные данные player_profiles и player_credentials.

Для этого необходимо в запросе использовать метод Include (и ThenInclude, если большая вложенность). Пример запроса приведен ниже.
public async Task<Subscriptions> GetWithProfile(string playerId)
{
return await RepositoryContext.Subscriptions
.Include(p => p.PlayerProfile)
.ThenInclude(c => c.PlayerCredential)
.FirstOrDefaultAsync(data => data.player_id == playerId);
}В результате такой метод сгенерирует один SQL запрос с JOIN, что позволяет эффективно получать данные.
SELECT s.player_id, s.expires_at, p.player_id, p.created_at, p.gender, p.name, p0.player_id, p0.email, p0.password
FROM subscriptions AS s
INNER JOIN player_profiles AS p ON s.player_id = p.player_id
INNER JOIN player_credentials AS p0 ON p.player_id = p0.player_id
WHERE s.player_id = @__playerId_0
LIMIT 1Отложенная загрузка позволяет автоматически загружать связанные данные при первом обращении к свойству. Чтобы использовать отложенную загрузку в Entity Framework Core, необходимо выполнить следующие шаги:
Убедитесь, что у вас установлен пакет
Microsoft.EntityFrameworkCore.Proxies.В методе конфигурации бд включите использование прокси-объектов:
services.AddDbContext<MyDbContext>(options => options.UseLazyLoadingProxies() .UseSqlServer(Configuration.GetConnectionString("...")));Все свойства, которые нужны для внешних ключей, должны быть помечены как
virtual, чтобы Entity Framework мог создать прокси-объекты и выполнять отложенную загрузку:[Table("downloads")] [PrimaryKey(nameof(player_id), nameof(map_id))] public class Downloads { [Required] [ForeignKey(nameof(PlayerProfile))] public string player_id { get; set; } [Required] [ForeignKey(nameof(CustomMap))] public string map_id { get; set; } public virtual PlayerProfiles PlayerProfile { get; set; } // virtual public virtual CustomMaps CustomMap { get; set; } // virtual }
Когда вы получите объект Downloads, доступ к downloads.PlayerProfile или downloads.CustomMap автоматически загрузит объект PlayerProfiles или CustomMaps соответственно, если его еще не загрузили.
Microsoft — EF Core — Beware of lazy loading
C# Corner — CRUD Operations In ASP.NET Core Web API Using ADO.NET
Автор документа: Артём Ветик