Как сопоставить свойство с правильными столбцами базы данных и корректными значениями, используя уже определенные в моделях преобразования.

Вопрос или проблема

Как сопоставить свойство с правильными столбцами базы данных и корректными значениями, используя уже определенные в моделях преобразования.

В своем приложении я добавляю фильтры на фронтенде. Фронтенд получит список всех возможных значений свойства для фильтрации. Чтобы получить все возможные значения, я получаю все уникальные значения из столбца базы данных.

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. Заключение

Использование сервиса для преобразования позволяет избежать дублирования кода и упрощает поддержку и расширение функциональности в будущем. Если вам нужно будет изменить логику преобразования, вам достаточно будет внести изменения в одном месте.

Такой подход делает код более чистым и согласованным, позволляя легко применять преобразования как на этапе получения уникальных значений, так и при фильтрации данных.

Оцените материал
Добавить комментарий

Капча загружается...