Как создать простое Rest API на .NET Core

Введение

Всем привет, в данной статье будет рассказано, как с использованием технологии C# ASP.NET Core написать простое Rest Api. Сделать Unit-тесты на слои приложений. Отправлять Json ответы. Также покажу, как выложить данное приложение в Docker.

В данной статье не будет описано, как делать клиентскую (далее Front) часть приложения. Здесь я покажу только серверную (далее Back).

Что используем?

Писать код я буду в Visual Studio 2019.

Для реализации приложения, я буду использовать такие библиотеки NuGet:

  1. Microsoft.EntityFrameworkCore

  2. Microsoft.EntityFrameworkCore.SqlServer

  3. Microsoft.EntityFrameworkCore.Tools

Для тестов вот эти библиотеки:

  1. Microsoft.NET.Test.Sdk

  2. Microsoft.NETCore.App

  3. Moq

  4. xunit

  5. xunit.runner.visualstudio

Для установки пакетов нужно зайти в обозреватель пакетов NuGet, сделать это можно, нажав ПКМ по проекту, и выбрав там пункт «управление пакетам NuGet»

Что программировать?

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

Настройка Базы Данных

Для настройки базы данных нужен класс ApplicationContext (реализация будет далее) и строка подключения, которая храниться в файле «appsettings.json». В этом классе будут прописаны все зависимости для генерации миграций. Строка подключения нужна для того, чтобы приложение знало в какую БД ей обращаться и с какими параметрами.

Чтобы добавить строку подключения, достаточно зайти в файл «appsettings.json» и прописать следующие строки:

"ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=testdb;Trusted_Connection=True;"
  },

Описание слоев приложения

Модели

В слое моделей будут находиться сущности, которые с помощью Entity Framework будут преобразованы в таблицы в базе данных.

Для описания модели в приложении достаточно просто описать класс, с нужными вам полями. Эти поля автоматически будут преобразованы в столбцы таблицы, а название таблицы будет соответствовать названию класса. Так задано по умолчанию, но есть специальные атрибуты, которые позволяют более гибко настраивать хранение данных в БД (но о них не в этой статье).

Первая модель, которая понадобиться для описания сервиса по ремонту - модель сотрудника. Что она будет из себя представлять?

  • Уникальный идентификатор сотрудника

  • Имя сотрудника

  • Должность сотрудника

  • Номер телефона для связи с сотрудником

Следующая модель для описания сервиса - автомобили, которые будут поступать на ремонт.

  • Уникальный идентификатор автомобиля

  • Название автомобиля

  • Номер автомобиля

И последняя модель, которую мы уже будем отсылать - документ (выписка) по ремонту.

  • Уникальный идентификатор документа

  • Сотрудник, который обслуживал автомобиль

  • Автомобиль, который был на ремонте

Чтобы модели попали в базу данных, необходимо создать миграцию. Миграция - описание того, как и что будет записано в базу данных. С помощью Entity Framework миграции можно генерировать автоматически. Для этого в пакетном менеджере надо прописать команду "Add-Migration". После этого Entity Framework сгенерирует миграцию по вашим моделям, которые указаны в классе DbContext. Чтобы применить миграцию, используем команду "Update-Database", после этого ваши данные попадут в базу данных (как это применять будет описано далее).

Контроллеры

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

Для возвращаемого значения в контроллерах будут использоваться тип Json. Для этого достаточно в return прописать

new JsonResult(Ваш объект)

В данном примере, я покажу как сделать методы для GET, POST, PUT и DELETE запросов. В GET-запросе я буду выбирать все существующие документы и передавать их на Front, а в POST-запросе я буду вызывать сервис по ремонту автомобиля и возвращать выписку по ремонту, PUT будет отвечать за обновление существующего документа и DELETE за удаление документа.

DAO (Репозитории)

Репозитории нужны как посредники для обеспечения работы с БД, чтобы исключить прямое взаимодействие человека с данными. Это нужно для того, чтобы сокрыть логику работы автоматизировать многие моменты работы с БД, а также для безопасной работы с данными.

В своем приложении я сделал репозиторий, который может принимать любую модель, и выполнять такие действия как get, get all, update, create, delete.

Сервисы

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

В качестве примера сервиса, я сделал класс, всего с одним методом Work. Этот метод имитирует работу моего сервиса по починке машин. В этом методе «нанимается» рабочий, заводится автомобиль и заполняется документ о его починке.

Реализация

Теперь, когда описано что и как будет устроено в приложении можно приступить и к реализации.

Создание проекта

При создании нового проекта, я выбрал веб-приложение ASP.NET Core, далее прописал его название (RestApi) и выбрал папку, где оно будет храниться. На экране выбора шаблона выбрал API.

Выбор шаблона приложения
Выбор шаблона приложения

Далее приступим к самому приложению.

Структура

Я разделил все приложение по папкам (также Unit-тесты в отдельном проекте) и получил вот такую структуру мое приложения:

Структура приложения
Структура приложения

Модели

Для реализации моделей я сделал абстрактный класс BaseModel. Он понадобиться в будущем для корректного наследования, а также в нем прописан Id каждой, модели (это помогает не дублировать код):

    public abstract class BaseModel
    {
        public Guid Id { get; set; }
    }

Далее вышеописанные модели:

    public class Car : BaseModel
    {
        public string Name { get; set; }
        public string Number { get; set; }
    }
    public class Document : BaseModel
    {
        public Guid CarId { get; set; }
        public Guid WorkerId { get; set; }
        public virtual Car Car { get; set; }
        public virtual Worker Worker { get; set; }
    }
  public class Worker : BaseModel
    {
        public string Name { get; set; }
        public string Position { get; set; }
        public string Telephone { get; set; }
    }

Репозиторий

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

Интерфейс:

public interface IBaseRepository<TDbModel> where TDbModel : BaseModel
    {
        public List<TDbModel> GetAll();
        public TDbModel Get(Guid id);
        public TDbModel Create(TDbModel model);
        public TDbModel Update(TDbModel model);
        public void Delete(Guid id);
    }

Реализация:

    public class BaseRepository<TDbModel> : IBaseRepository<TDbModel> where TDbModel : BaseModel
    {
        private ApplicationContext Context { get; set; }
        public BaseRepository(ApplicationContext context)
        {
            Context = context;
        }

        public TDbModel Create(TDbModel model)
        {
            Context.Set<TDbModel>().Add(model);
            Context.SaveChanges();
            return model;
        }

        public void Delete(Guid id)
        {
            var toDelete = Context.Set<TDbModel>().FirstOrDefault(m => m.Id == id);
            Context.Set<TDbModel>().Remove(toDelete);
            Context.SaveChanges();
        }

        public List<TDbModel> GetAll()
        {
            return Context.Set<TDbModel>().ToList();
        }

        public TDbModel Update(TDbModel model)
        {
            var toUpdate = Context.Set<TDbModel>().FirstOrDefault(m => m.Id == model.Id);
            if (toUpdate != null)
            {
                toUpdate = model;
            }
            Context.Update(toUpdate);
            Context.SaveChanges();
            return toUpdate;
        }

        public TDbModel Get(Guid id)
        {
            return Context.Set<TDbModel>().FirstOrDefault(m => m.Id == id);
        }
    }

Сервис

Сервис также как и репозиторий имеет интерфейс и его реализацию.

Интерфейс:

public interface IRepairService
    {
        public void Work();
    }

Реализация:

public class RepairService : IRepairService
    {
        private IBaseRepository<Document> Documents { get; set; }
        private IBaseRepository<Car> Cars { get; set; }
        private IBaseRepository<Worker> Workers { get; set; }

        public void Work()
        {
            var rand = new Random();
            var carId = Guid.NewGuid();
            var workerId = Guid.NewGuid();

            Cars.Create(new Car
            {
                Id = carId,
                Name = String.Format($"Car{rand.Next()}"),
                Number = String.Format($"{rand.Next()}")
            });

            Workers.Create(new Worker
            {
                Id = workerId,
                Name = String.Format($"Worker{rand.Next()}"),
                Position = String.Format($"Position{rand.Next()}"),
                Telephone = String.Format($"8916{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}")
            });

            var car = Cars.Get(carId);
            var worker = Workers.Get(workerId);

            Documents.Create(new Document {
                CarId = car.Id,
                WorkerId = worker.Id,
                Car = car,
                Worker = worker
            });
        }
    }

Контроллер

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

ДоменноеИмя/НазваниеКонтроллера/НазваниеМетода?Параметры(если есть)

Пути гибко настраиваются с помощью специальных атрибутов (о них не в этой статье).

Мой MainController:

[ApiController]
    [Route("[controller]")]
    public class MainController : ControllerBase
    {
        private IRepairService RepairService { get; set; }
        private IBaseRepository<Document> Documents { get; set; }

        public MainController(IRepairService repairService, IBaseRepository<Document> document )
        {
            RepairService = repairService;
            Documents = document;
        }

        [HttpGet]
        public JsonResult Get()
        {
            return new JsonResult(Documents.GetAll());
        }

        [HttpPost]
        public JsonResult Post()
        {
            RepairService.Work();
            return new JsonResult("Work was successfully done");
        }

        [HttpPut]
        public JsonResult Put(Document doc)
        {
            bool success = true;
            var document = Documents.Get(doc.Id);
            try
            {
                if (document != null)
                {
                    document = Documents.Update(doc);
                }
                else
                {
                    success = false;
                }
            }
            catch (Exception)
            {
                success = false;
            }

            return success ? new JsonResult($"Update successful {document.Id}") : new JsonResult("Update was not successful");
        }

        [HttpDelete]
        public JsonResult Delete(Guid id)
        {
            bool success = true;
            var document = Documents.Get(id);

            try
            {
                if (document != null)
                {
                    Documents.Delete(document.Id);
                }
                else
                {
                    success = false;
                }
            }
            catch (Exception)
            {
                success = false;
            }

            return success ? new JsonResult("Delete successful") : new JsonResult("Delete was not successful");
        }
    }

Application Context

ApplicationContext – класс, который унаследован от класса DbContext. В нем прописываются все DbSet. С их помощью приложение знает, какие модели должны быть в базе данных, а какие нет.

public class ApplicationContext: DbContext
    {
        public DbSet<Car> Cars { get; set; }
        public DbSet<Document> Documents { get; set; }
        public DbSet<Worker> Workers { get; set; }

        public ApplicationContext(DbContextOptions<ApplicationContext> options): base(options)
        {
            Database.EnsureCreated();
        }
    }

Настройка зависимостей и инжектирования

А теперь немного про инжектирование. Правильная настройка зависимостей проекта Asp.net core позволяет упростить его работу и избежать лишнего написания кода. Все зависимости прописываются в файле «Startup.cs».

Что я связывал? Я связывал интерфейс репозитория с репозиторием каждой модели (далее будет видно, что имеется ввиду), также я связал интерфейс сервиса с его реализацией.

Также в этом же файле прописываются настройки для базы данных. Помните про строку подключения из начала статьи? Так вот сейчас мы ее и используем для настройки БД.

Вот как выглядит мой файл «Startup.cs»:

public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            string connection = Configuration.GetConnectionString("DefaultConnection");
            services.AddMvc();
            services.AddDbContext<ApplicationContext>(options =>
                options.UseSqlServer(connection));

            services.AddTransient<IRepairService, RepairService>();
            services.AddTransient<IBaseRepository<Document>, BaseRepository<Document>>();
            services.AddTransient<IBaseRepository<Car>, BaseRepository<Car>>();
            services.AddTransient<IBaseRepository<Worker>, BaseRepository<Worker>>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

Не забудьте создать БД перед запуском приложения. Для этого в Консоле диспетчера пакетов нужно прописать следующие команды:

Add-Migration init (или любое другое имя)

Update-Database

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

Тестирование

Здесь я покажу как создать UNIT-тесты для контроллера и сервиса. Для тестов я сделал отдельный проект (библиотека классов .Net Core).

Тест для контроллера

public class MainControllerTests
    {
        [Fact]
        public void GetDataMessage()
        {
            var mockDocs = new Mock<IBaseRepository<Document>>();
            var mockService = new Mock<IRepairService>();
            var document = GetDoc();
            mockDocs.Setup(x => x.GetAll()).Returns(new List<Document> { document });

            // Arrange
            MainController controller = new MainController(mockService.Object, mockDocs.Object);

            // Act
            JsonResult result = controller.Get() as JsonResult;

            // Assert
            Assert.Equal(new List<Document> { document }, result?.Value);
        }

        [Fact]
        public void GetNotNull()
        {
            var mockDocs = new Mock<IBaseRepository<Document>>();
            var mockService = new Mock<IRepairService>();
            mockDocs.Setup(x => x.Create(GetDoc())).Returns(GetDoc());

            // Arrange
            MainController controller = new MainController(mockService.Object, mockDocs.Object);
            // Act
            JsonResult result = controller.Get() as JsonResult;
            // Assert
            Assert.NotNull(result);
        }

        [Fact]
        public void PostDataMessage()
        {
            var mockDocs = new Mock<IBaseRepository<Document>>();
            var mockService = new Mock<IRepairService>();
            mockDocs.Setup(x => x.Create(GetDoc())).Returns(GetDoc());

            // Arrange
            MainController controller = new MainController(mockService.Object, mockDocs.Object);

            // Act
            JsonResult result = controller.Post() as JsonResult;

            // Assert
            Assert.Equal("Work was successfully done", result?.Value);
        }

        [Fact]
        public void UpdateDataMessage()
        {
            var mockDocs = new Mock<IBaseRepository<Document>>();
            var mockService = new Mock<IRepairService>();
            var document = GetDoc();

            mockDocs.Setup(x => x.Get(document.Id)).Returns(document);
            mockDocs.Setup(x => x.Update(document)).Returns(document);

            // Arrange
            MainController controller = new MainController(mockService.Object, mockDocs.Object);

            // Act
            JsonResult result = controller.Put(document) as JsonResult;

            // Assert
            Assert.Equal($"Update successful {document.Id}", result?.Value);
        }

        [Fact]
        public void DeleteDataMessage()
        {
            var mockDocs = new Mock<IBaseRepository<Document>>();
            var mockService = new Mock<IRepairService>();
            var doc = GetDoc();

            mockDocs.Setup(x => x.Get(doc.Id)).Returns(doc);
            mockDocs.Setup(x => x.Delete(doc.Id));

            // Arrange
            MainController controller = new MainController(mockService.Object, mockDocs.Object);

            // Act
            JsonResult result = controller.Delete(doc.Id) as JsonResult;

            // Assert
            Assert.Equal("Delete successful", result?.Value);
        }

        public Document GetDoc()
        {
            var mockCars = new Mock<IBaseRepository<Car>>();
            var mockWorkers = new Mock<IBaseRepository<Worker>>();

            var carId = Guid.NewGuid();
            var workerId = Guid.NewGuid();
            mockCars.Setup(x => x.Create(new Car()
            {
                Id = carId,
                Name = "car",
                Number = "123"
            }));

            mockWorkers.Setup(x => x.Create(new Worker()
            {
                Id = workerId,
                Name = "worker",
                Position = "manager",
                Telephone = "89165555555"
            }));

            return new Document
            {
                Id = Guid.NewGuid(),
                CarId = carId,
                WorkerId = workerId
            };
        }
    }

В данных тестах проверяется работа каждого метода контроллера на их корректное выполнение.

Тест для сервиса

public class RepairServiceTests
    {
        [Fact]
        public void WorkSuccessTest()
        {
            var serviceMock = new Mock<IRepairService>();
            var mockCars = new Mock<IBaseRepository<Car>>();
            var mockWorkers = new Mock<IBaseRepository<Worker>>();
            var mockDocs = new Mock<IBaseRepository<Document>>();
            var car = CreateCar(Guid.NewGuid());
            var worker = CreateWorker(Guid.NewGuid());
            var doc = CreateDoc(Guid.NewGuid(), worker.Id, car.Id);

            mockCars.Setup(x => x.Create(car)).Returns(car);
            mockDocs.Setup(x => x.Create(doc)).Returns(doc);
            mockWorkers.Setup(x => x.Create(worker)).Returns(worker);

            serviceMock.Object.Work();

            serviceMock.Verify(x => x.Work());
        }

        private Car CreateCar(Guid carId)
        {
            return new Car()
            {
                Id = carId,
                Name = "car",
                Number = "123"
            };
        }

        private Worker CreateWorker(Guid workerId)
        {
            return new Worker()
            {
                Id = workerId,
                Name = "worker",
                Position = "manager",
                Telephone = "89165555555"
            };
        }
        private Document CreateDoc(Guid docId, Guid workerId, Guid carId)
        {
            return new Document
            {
                Id = docId,
                CarId = carId,
                WorkerId = workerId
            };
        }
    }

В тесте для сервиса есть всего один тест для метода Work. Тут проверяется отработал этот метод или нет.

Запуск тестов

Чтобы запустить тесты достаточно зайти во вкладку «Тест» и нажать выполнить все тесты.

Выкладываем в Docker

В финале я покажу, как выложить данное приложение в Docker Hub. В Visual Studio 2019 это сделать крайне просто. Учтите, что у вас уже должен быть профиль в Docker и создан репозиторий в Docker Hub.

Нажимаете ПКМ на ваш проект и выбираете пункт опубликовать.

Там выбираем Docker Container Registry

На следующем окне, надо выбрать Docker Hub

Далее введите свои учетные данный Docker.

Если все прошло успешно, то осталось сделать последнюю вещь, нажать кнопку «Опубликовать».

Готово, вы опубликовали свое приложение в Docker Hub!

Заключение

В данной статье я показал, как использовать возможности C# ASP.NET Core для создания простого Rest API. Показал, как создавать модели, записывать их в БД, как создать свой репозиторий, как использовать сервисы и как создавать контроллеры, которые будут отправлять JSON ответы на ваш Front. Также показал, как сделать Unit-тесты для слоев контроллеров и сервисов. И в финале показал, как выложить приложение в Docker.

Надеюсь, что данная статья будет вам полезна!

Средняя зарплата в IT

120 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 3 348 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
Реклама
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее

Комментарии 9

    +3

    Почему был выбран JsonResult?


    return success ? new JsonResult($"Update successful {document.Id}") : new JsonResult("Update was not successful")

    Т.е результат вроде бы 200 ОК, а вроде и нет.
    Иногда сталкиваюсь с преимущественно старыми системами, которые возвращают ошибки с 200-м кодом ответа Http. Столько "радости" с ними интегрироваться.


    В этом случае правильнее использовать наследников StatusCodeResult, например BadRequestResult,OkResult.

      +3
      Тестирование

      Что конкретно вы тестируете и зачем?

        +9
        Не статья, а сборник вредных советов.
        1. Зачем вызывать Context.SaveChanges(); в методах BaseRepository? Как вы откатите Worker и Car, если упал Document.Create?
        2. services.AddTransient<IBaseRepository<*>, BaseRepository<*>>(); Зачем, если DbContext — Scoped по умолчанию?
        3. Зачем использовать JsonResut, если есть Ok(), Created(), BadRequest() и т.д.?
        4. При поиске по первичному ключу желательно использовать Find, а не FirstOrDefault
        5. Очень странное решение инжектить репозитории в сервисе через свойства, а не конструктор
        6. В Delete два раза загружается entity
        7. Возвращать из контроллера entity — плохая идея
        8. В тесте — это что вообще такое? )))
          serviceMock.Object.Work(); 
          
          serviceMock.Verify(x => x.Work());
          +3
          Вот действительно, не надо так!
          1. В статье куча всего (вредного) кроме самого Rest API
          2. Rest API реализован с ошибками (ошибка обработки должна возвращать ошибочный статус, почему-то get только на список, а как получить один экземпляр? Post и вовсе ничего не принимает на вход)
          3. Да и вообще, реализацию API стоит начинать с описание этого API.
          4. Зачем писать тест, который ничего не тестирует?
          5-12. пункты из предыдущего комментария.
            +2
            Уважаемые авторы!

            Большая просьба, не писать статьи если вы действительно не обладаете опытом в данной теме. И тем более, не нужно писать статьи «для новичков» ведь именно они-то и не могут отделять зерен от плевел и в результате начинают тупо копировать странные и вредные практики.

            Если по существу, запомните золотое правило архитектуры ПО — не усложняйте архитектуру без надобности. Например, зачем необходим

            public abstract class BaseModel
            {
                public Guid Id { get; set; }
            }

            Какие задачи он упрощает потом? Что будет, если мне нужна будет сущность с интовым Id? Я понимаю, если бы было сделано что-то типа этого:

            
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                foreach (var entityType in modelBuilder.Model.GetEntityTypes())
                {
                    if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
                    {
                        entityType.AddSoftDeleteQueryFilter();      
                    }    
                }
            }
            public static void AddSoftDeleteQueryFilter(
                    this IMutableEntityType entityData)
                {
                    var methodToCall = typeof(SoftDeleteQueryExtension)
                        .GetMethod(nameof(GetSoftDeleteFilter),
                            BindingFlags.NonPublic | BindingFlags.Static)
                        .MakeGenericMethod(entityData.ClrType);
                    var filter = methodToCall.Invoke(null, new object[] { });
                    entityData.SetQueryFilter((LambdaExpression)filter);
                }
             
                private static LambdaExpression GetSoftDeleteFilter<TEntity>()
                    where TEntity : class, ISoftDelete
                {
                    Expression<Func<TEntity, bool>> filter = x => !x.SoftDeleted;
                    return filter;
                }
            

            В чем тут профит — понятно. Наследуя один раз интерфейс, мы под капотом оставляем кучу работы связанной со скрытием «удаленных» сущностей. Аналогично можно сделать тинанты или аудит. Здесь же мы просто не пишем один раз проперти, зато пишем наследование! Фантастика!

            Аналогична ситуация и с

            public interface IBaseRepository<TDbModel> where TDbModel : BaseModel
            {
                    public List<TDbModel> GetAll();
                    public TDbModel Get(Guid id);
                    public TDbModel Create(TDbModel model);
                    public TDbModel Update(TDbModel model);
                    public void Delete(Guid id);
            }

            В чем смысл данной сущности? Что она дает такого нового, что отлично от DbSet? В ней нет никакого смысла, просто еще один слой абстракции который еще потом требует вот такого чуда:

            services.AddTransient<IBaseRepository<Car>, BaseRepository<Car>>();

            А теперь представьте проект с сотнями моделей? Мы просто создали себе дополнительную работу и не решили ни одной проблемы. Например, нам нужно использовать какой-нибудь хитрый фильтр по автомобилям в трех различных местах. Логично вынести этот фильтр в отдельный сервис для доступа к данным тем самым не повторяя код. Но в данных примерах этого нет, там простое повторение DbSet<>.

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

            image

              0
              Ребята, может я слишком стар и не модный. Но я еще помню, что я писал первое приложение, что писал первую статью, что читал первый пост на хабре. И вот, когда я перешел в комментарии к первому посту, я тогда увидел как правильно надо делать, а не что было в статье не плохо ( об этом я смог догадаться сам).
              Так вот, ребята, я понимаю, что хейтить модно, и если автор найдет, как это надо делать сам, то запомнить лучше. Но если все будут хейтить, то и искать будет негде ))
                0
                Как надо делать описать гораздо сложнее. Потому что жизнь — не лабораторная работа, и там нет универсального правильного ответа. Опыт как раз таки и говорит, что в разных случаях надо бывает разным. За этот опыт как раз деньги и платить должны, а не за парсинг json.

                Если будет время, я попробую написать пару статей, как можно было бы это писать в тех или иных случаях.
                  0
                  Проблема в том, что тут, в этой статье, нет ничего от правильного.
                  Точнее есть только один хороший пункт, соответствующий заголовку — это какой тип проекта создавать. Да и то, стоило бы показать, что создаётся автоматом, и как надо сразу переделывать эту болванку:
                  using System;
                  using System.Collections.Generic;
                  using System.Linq;
                  using System.Threading.Tasks;
                  using Microsoft.AspNetCore.Mvc;
                  using Microsoft.Extensions.Logging;
                  
                  namespace WebApi1.Controllers
                  {
                      [ApiController]
                      [Route("[controller]")]
                      public class WeatherForecastController : ControllerBase
                      {
                          private static readonly string[] Summaries = new[]
                          {
                              "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
                          };
                  
                          private readonly ILogger<WeatherForecastController> _logger;
                  
                          public WeatherForecastController(ILogger<WeatherForecastController> logger)
                          {
                              _logger = logger;
                          }
                  
                          [HttpGet]
                          public IEnumerable<WeatherForecast> Get()
                          {
                              var rng = new Random();
                              return Enumerable.Range(1, 5).Select(index => new WeatherForecast
                              {
                                  Date = DateTime.Now.AddDays(index),
                                  TemperatureC = rng.Next(-20, 55),
                                  Summary = Summaries[rng.Next(Summaries.Length)]
                              })
                              .ToArray();
                          }
                      }
                  }
                  


                  Вот сразу прямо тут можно улучшать, чтобы по шагам превратить в то самое «простое rest api».
                  Первый шаг, не бесспорный:

                  using System;
                  using System.Collections.Generic;
                  using System.Linq;
                  using System.Threading.Tasks;
                  using Microsoft.AspNetCore.Mvc;
                  using Microsoft.Extensions.Logging;
                  
                  namespace WebApi1.Controllers
                  {
                      [ApiController]
                      [Route("[controller]")]
                      public class WeatherForecastController : ControllerBase
                      {
                          private static Random Rng = new Random();
                          private WeatherForecast[] forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
                          {
                              Date = DateTime.Now.AddDays(index),
                              TemperatureC = Rng.Next(-20, 55),
                              Summary = Summaries[Rng.Next(Summaries.Length)]
                          }).ToArray();
                  
                          private static readonly string[] Summaries = new[]
                          {
                              "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
                          };
                  
                          private readonly ILogger<WeatherForecastController> _logger;
                  
                          public WeatherForecastController(ILogger<WeatherForecastController> logger)
                          {
                              _logger = logger;
                          }
                  
                          /// <summary>
                          /// Get list of forecasts
                          /// </summary>
                          /// <returns></returns>
                          [HttpGet]
                          public IEnumerable<WeatherForecast> Get()
                          {
                              _logger.LogInformation("Get list of forecasts");
                              return forecasts;
                          }
                  
                          /// <summary>
                          /// Get one of forecasts or 404 error
                          /// </summary>
                          /// <param name="num"></param>
                          /// <returns></returns>
                          [HttpGet]
                          public ActionResult<WeatherForecast> Get(int num)
                          {
                              _logger.LogInformation($"Get forecasts[{num}]");
                  
                              if (num >= 0 && num < forecasts.Length)
                                  return forecasts[num];
                              else
                                  return NotFound();
                          }
                      }
                  }
                  


                  И далее по вкусу…
                  0
                  Если сейчас в универах такие лабораторные — моё почтение.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое