Вопрос или проблема
Как сопоставить свойство с правильными столбцами базы данных и корректными значениями, используя уже определенные в моделях преобразования.
В своем приложении я добавляю фильтры на фронтенде. Фронтенд получит список всех возможных значений свойства для фильтрации. Чтобы получить все возможные значения, я получаю все уникальные значения из столбца базы данных.
var possibleNames = await Article.Select(x => x.Name).Distinct().ToListAsync();
Когда фильтр выбран, его можно просто добавить в условие where.
var articles = await Article.Where(x => x.Name == name).ToListAsync();
Проблема в том, что не все свойства, которые передаются на фронтенд, являются столбцами в базе данных. Чтобы продемонстрировать это, я сделал пример для статьи.
У меня есть приложение, в котором есть 3 класса для каждой модели.
- Модель базы данных для взаимодействия с базой данных
- Модель DTO для внутреннего использования в приложении
- Модель представления для отправки через HTTP-запрос
Каждая модель может изменять свойства при создании.
Enum Type {
Magazine,
Store
}
public class ArticleDB
{
public int Id { get; set; }
public string Name { get; set; }
public int LengthInMM { get; set; }
public bool MagazineRack { get; set; }
public bool StoreRack { get; set; }
public ArticleDB(ArticleDTO dto)
{
Id = dto.Id;
Name = dto.Name;
LengthInMM = dto.LengthInMM;
MagazineRack = dto.StoredIn == Type.Magazine;
StoreRack = dto.StoredIn == Type.Store;
}
public ArticleDTO ToDTO() => new ArticleDTO()
{
Id = Id,
Name = Name,
LengthInMM = LengthInMM,
StoredIn = MagazineRack ? Type.Magazine : Type.Store
}
}
public class ArticleDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int LengthInMM { get; set; }
public Type StoredIn { get; set; }
}
public class ArticleView
{
public int Id { get; set; }
public string Name { get; set; }
public int LengthInM { get; set; }
public string StoredIn { get; set; }
public ArticleView(ArticleDTO dto)
{
Id = dto.Id;
Name = dto.Name;
LengthInM = dto.LengthInMM / 1000;
StoredIn = dto.StoredIn.ToString();
}
public ArticleDTO ToDTO() => new ArticleDTO()
{
Id = Id,
Name = Name,
LengthInMM = LengthInM * 1000,
StoredIn = StoredIn == "Magazine" ? Type.Magazine : Type.Store
}
}
Существует ли чистый способ использовать эти модели для преобразования отдельных свойств в один или несколько столбцов и преобразования значения, если это необходимо, для получения возможных значений и для получения отфильтрованных значений? В данный момент, если я хочу фильтровать по LengthInM
, мне приходится преобразовывать это значение в 3 разных местах: при получении значений, при получении возможных значений, при фильтрации по значению. Также для StoredIn
нужно будет сделать пользовательскую проверку в 3 разных местах.
Ответ или решение
Чтобы правильно сопоставить свойства из вашего приложения с соответствующими столбцами базы данных, с учетом необходимых преобразований, мы можем создать централизованную логику преобразования, которая будет использоваться как для получения уникальных значений, так и для фильтрации.
1. Общая Структура Классов
Как мы видим в вашем примере, у вас есть три модели: ArticleDB
, ArticleDTO
и ArticleView
. Чтобы упростить работу с ними, можно создать специальный сервис, который будет обрабатывать преобразования. Основная идея заключается в использовании методов для работы со свойствами, позволяющих свести повторяющийся код к минимуму.
2. Реализация Сервиса Преобразования
Создадим класс ArticleFilterService
, который будет иметь методы для преобразования свойств.
public class ArticleFilterService
{
public string GetStoredInValue(Type storedIn)
{
return storedIn == Type.Magazine ? "Magazine" : "Store";
}
public Type ParseStoredInValue(string storedIn)
{
return storedIn == "Magazine" ? Type.Magazine : Type.Store;
}
public double ConvertLengthInMToMM(double lengthInM)
{
return lengthInM * 1000;
}
public double ConvertLengthInMMToM(int lengthInMM)
{
return lengthInMM / 1000.0;
}
public async Task<List<string>> GetPossibleValuesForLength(ArticleContext context)
{
var possibleLengths = await context.Articles
.Select(x => ConvertLengthInMMToM(x.LengthInMM))
.Distinct()
.ToListAsync();
return possibleLengths.Select(l => l.ToString()).ToList();
}
public async Task<List<string>> GetPossibleValuesForStoredIn(ArticleContext context)
{
var possibleStoredInValues = await context.Articles
.Select(x => GetStoredInValue(x.StoredIn))
.Distinct()
.ToListAsync();
return possibleStoredInValues;
}
}
3. Использование Сервиса в Контроллерах
Теперь, при получении возможных значений или при фильтрации, мы можем использовать методы из сервиса ArticleFilterService
:
public class ArticleController : ControllerBase
{
private readonly ArticleFilterService _filterService;
private readonly ArticleContext _context;
public ArticleController(ArticleFilterService filterService, ArticleContext context)
{
_filterService = filterService;
_context = context;
}
public async Task<IActionResult> GetPossibleLengthValues()
{
var possibleValues = await _filterService.GetPossibleValuesForLength(_context);
return Ok(possibleValues);
}
public async Task<IActionResult> GetPossibleStoredInValues()
{
var possibleValues = await _filterService.GetPossibleValuesForStoredIn(_context);
return Ok(possibleValues);
}
public async Task<IActionResult> FilterArticles(string name, string storedIn, double? lengthInM)
{
var query = _context.Articles.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(x => x.Name == name);
}
if (!string.IsNullOrEmpty(storedIn))
{
var storedInType = _filterService.ParseStoredInValue(storedIn);
query = query.Where(x => x.StoredIn == storedInType);
}
if (lengthInM.HasValue)
{
var lengthInMM = _filterService.ConvertLengthInMToMM(lengthInM.Value);
query = query.Where(x => x.LengthInMM == lengthInMM);
}
var articles = await query.ToListAsync();
return Ok(articles);
}
}
4. Заключение
Использование сервиса для преобразования позволяет избежать дублирования кода и упрощает поддержку и расширение функциональности в будущем. Если вам нужно будет изменить логику преобразования, вам достаточно будет внести изменения в одном месте.
Такой подход делает код более чистым и согласованным, позволляя легко применять преобразования как на этапе получения уникальных значений, так и при фильтрации данных.