Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Уменьшение количества бойлерплейт кода в webapi-приложении

#c_sharp #api #архитектура #aspnet_web_api


Пишу клиент и сервер для проекта. Суть проекта: складской учет, возможно в будущем
будет расширяться с добавлением бухгалтерского. То есть склады, документы прихода,
расхода, перемещения, зарплата.

Имеется схема: C# desktop - WEB API Asp.Net Core - Database MySQL

Клиент посылает запрос на сервер, сервер обращается к БД и возвращается JSON. Через
HttpClient.

Схема MVC.
Пока протестировал на одной табличке. В контроллере прописал API(для примера):

    // GET: api/Products
    [HttpGet]
    public async Task>> GetProduct()
    {
        return await _context.Product.ToListAsync();
    }

    // GET: api/Products/5
    [HttpGet("{id}")]
    public async Task> GetProduct(int id)
    {
        var product = await _context.Product.FindAsync(id);

        if (product == null)
        {
            return NotFound();
        }

        return product;
    }



Подскажите, когда табличек будет много, и вариантов запросов будет
много, то необходимо каждый запрос вот так вот прописывать(по типу
того что в коде)?
Есть ли какое-то более оптимальное решение и все ли я делаю
правильно?

То есть в будущем я планирую для каждой таблицы создать контроллер и в нем прописывать
api запросы для сервера.

    


Ответы

Ответ 1



Вы делаете всё правильно, но для типовых операций придумали такую вещь, как скафолдинг (scaffolding). Настраивается это следующим образом. В папке C:\Users\{username}\.nuget\packages\microsoft.visualstudio.web.codegenerators.mvc\2.2.2\Templates (у вас могут быть и папки 2.2.1 и прочие) находятся шаблоны предоставляемые майкрософт по умолчанию вместе со студией, например: @inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @{string modelName = (Model.ClassName.EndsWith("Controller") ? Model.ClassName.Substring(0, Model.ClassName.Length - 10) : Model.ClassName);} namespace @Model.NamespaceName { [Route("api/[controller]")] [ApiController] public class @Model.ClassName : ControllerBase { // GET: api/@modelName [HttpGet] public IEnumerable Get() { return new string[] { "value1", "value2" }; } // GET: api/@modelName/5 [HttpGet("{id}", Name = "Get")] public string Get(int id) { return "value"; } // POST: api/@modelName [HttpPost] public void Post([FromBody] string value) { } // PUT: api/@modelName/5 [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } // DELETE: api/ApiWithActions/5 [HttpDelete("{id}")] public void Delete(int id) { } } } Рекомендую не трогать шаблоны по умолчанию, а скопировать папку Templates в ваш проект и уже там отредактировать так, как вам необходимо: Ссылки по теме: Scaffold: beginners guide Metanit: Scaffolding в Visual Studio Custom scaffold templates in ASP.NET Core

Ответ 2



Почти наверняка большинство таблиц будет требовать одинаковых алгоритмов: получать энтити по ключу, получать их список по некоторому признаку с разбиением по страницам и т.п. Значит, этот код нужно как-то абстрагировать от типа таблицы. Вы можете абстрагировать DAL (унести DbContext и получение данных из контроллера в другое место). Какие-то базовые операции вынести в обобщенный код (например, базовый репозиторий, который умеет простой CRUD для любой таблицы), специфичные части запроса делать в наследниках, строя запрос по частям с помощью IQueriable. Завести сущность фильтра и передать ее в методы репозитория, где на основе его уже будет строиться запрос. Аналогичным образом можно и интерфейсный слой (контроллеры) сделать обобщенными, с вкраплением кастомной логики.

Ответ 3



Подскажите, когда табличек будет много, и вариантов запросов будет много, то необходимо каждый запрос вот так вот прописывать(по типу того что в коде)? Смотря сколько параметров в запросе. Если их мало, проще прописывать каждый по отдельности. Если их будет много, лучше использовать DTO (Data Transfer Object) и структурировать параметры в нём. То есть в будущем я планирую для каждой таблицы создать контроллер и в нем прописывать api запросы для сервера. Если у Вас на клиенте предусмотрено изменение всех таблиц, то Вам возможно придётся поступить именно так. И то, в зависимости от ситуации можно построить общую точку редактирования нескольких таблиц. А, вообще такой подход чаще всего не оправдан. Хотя бы потому, что не все таблицы нужно редактировать. например, таблицы, которые хранят перечисления. Правда, если совсем по-хорошему, следует ориентироваться на модель предметной области и уже исходя из неё проектировать систему.

Комментариев нет:

Отправить комментарий