From cc4254d264710f6cf32b59bc045c5cc958ff6e02 Mon Sep 17 00:00:00 2001 From: NikitaLyubimov <55978147+m11s1ma@users.noreply.github.com> Date: Wed, 26 Apr 2023 03:30:04 +0300 Subject: [PATCH 1/2] refactoring --- Controllers/TodoItemsController.cs | 116 ------------------ Models/TodoContext.cs | 14 --- Models/TodoItem.cs | 12 -- TodoApiDTO.sln | 32 +++-- .../ApplicationCollectionExtension.cs | 19 +++ .../Services/AddTodoItemService.cs | 27 ++++ .../Services/DeleteTodoItemService.cs | 49 ++++++++ .../Services/GetTodoItemService.cs | 35 ++++++ .../Services/GetTodoItemsService.cs | 26 ++++ .../Services/UpdateTodoItemService.cs | 49 ++++++++ TodoApplication/TodoApplication.csproj | 16 +++ {Models => TodoCore/DTOs}/TodoItemDTO.cs | 6 +- TodoCore/Data/Common/BaseEntity.cs | 7 ++ TodoCore/Data/Entities/TodoItem.cs | 10 ++ TodoCore/Data/Interfaces/IReposytory.cs | 16 +++ .../Data/Interfaces/ITodoItemReposytory.cs | 9 ++ TodoCore/Data/Interfaces/IUnitOfWork.cs | 12 ++ .../Exceptions/EntityNotFoundException.cs | 13 ++ .../Exceptions/SomethingWentWrongException.cs | 9 ++ TodoCore/Services/IAddTodoItemService.cs | 10 ++ TodoCore/Services/IDeleteTodoItemService.cs | 10 ++ TodoCore/Services/IGetTodoItemService.cs | 10 ++ TodoCore/Services/IGetTodoItemsService.cs | 11 ++ TodoCore/Services/IUpdateTodoItemService.cs | 13 ++ TodoCore/TodoCore.csproj | 7 ++ .../DataAccess/ApplicationDbContext.cs | 12 ++ .../DataAccess/Repositories/Reposytory.cs | 56 +++++++++ .../Repositories/TodoItemReposytory.cs | 10 ++ .../DataAccess/UOW/UnitOfWork.cs | 34 +++++ .../InfrastructureCollectionExtension.cs | 25 ++++ TodoInfrastructure/TodoInfrastructure.csproj | 17 +++ .../Controllers/TodoItemsController.cs | 95 ++++++++++++++ Program.cs => VeletechToDoAPI/Program.cs | 0 .../Properties}/launchSettings.json | 0 README.md => VeletechToDoAPI/README.md | 0 Startup.cs => VeletechToDoAPI/Startup.cs | 18 ++- .../TodoApiDTO.csproj | 9 +- .../appsettings.Development.json | 0 .../appsettings.json | 0 39 files changed, 656 insertions(+), 158 deletions(-) delete mode 100644 Controllers/TodoItemsController.cs delete mode 100644 Models/TodoContext.cs delete mode 100644 Models/TodoItem.cs create mode 100644 TodoApplication/ApplicationCollectionExtension.cs create mode 100644 TodoApplication/Services/AddTodoItemService.cs create mode 100644 TodoApplication/Services/DeleteTodoItemService.cs create mode 100644 TodoApplication/Services/GetTodoItemService.cs create mode 100644 TodoApplication/Services/GetTodoItemsService.cs create mode 100644 TodoApplication/Services/UpdateTodoItemService.cs create mode 100644 TodoApplication/TodoApplication.csproj rename {Models => TodoCore/DTOs}/TodoItemDTO.cs (53%) create mode 100644 TodoCore/Data/Common/BaseEntity.cs create mode 100644 TodoCore/Data/Entities/TodoItem.cs create mode 100644 TodoCore/Data/Interfaces/IReposytory.cs create mode 100644 TodoCore/Data/Interfaces/ITodoItemReposytory.cs create mode 100644 TodoCore/Data/Interfaces/IUnitOfWork.cs create mode 100644 TodoCore/Exceptions/EntityNotFoundException.cs create mode 100644 TodoCore/Exceptions/SomethingWentWrongException.cs create mode 100644 TodoCore/Services/IAddTodoItemService.cs create mode 100644 TodoCore/Services/IDeleteTodoItemService.cs create mode 100644 TodoCore/Services/IGetTodoItemService.cs create mode 100644 TodoCore/Services/IGetTodoItemsService.cs create mode 100644 TodoCore/Services/IUpdateTodoItemService.cs create mode 100644 TodoCore/TodoCore.csproj create mode 100644 TodoInfrastructure/DataAccess/ApplicationDbContext.cs create mode 100644 TodoInfrastructure/DataAccess/Repositories/Reposytory.cs create mode 100644 TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs create mode 100644 TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs create mode 100644 TodoInfrastructure/InfrastructureCollectionExtension.cs create mode 100644 TodoInfrastructure/TodoInfrastructure.csproj create mode 100644 VeletechToDoAPI/Controllers/TodoItemsController.cs rename Program.cs => VeletechToDoAPI/Program.cs (100%) rename {Properties => VeletechToDoAPI/Properties}/launchSettings.json (100%) rename README.md => VeletechToDoAPI/README.md (100%) rename Startup.cs => VeletechToDoAPI/Startup.cs (75%) rename TodoApiDTO.csproj => VeletechToDoAPI/TodoApiDTO.csproj (64%) rename appsettings.Development.json => VeletechToDoAPI/appsettings.Development.json (100%) rename appsettings.json => VeletechToDoAPI/appsettings.json (100%) diff --git a/Controllers/TodoItemsController.cs b/Controllers/TodoItemsController.cs deleted file mode 100644 index 0ef138e7..00000000 --- a/Controllers/TodoItemsController.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using TodoApi.Models; - -namespace TodoApi.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class TodoItemsController : ControllerBase - { - private readonly TodoContext _context; - - public TodoItemsController(TodoContext context) - { - _context = context; - } - - [HttpGet] - public async Task>> GetTodoItems() - { - return await _context.TodoItems - .Select(x => ItemToDTO(x)) - .ToListAsync(); - } - - [HttpGet("{id}")] - public async Task> GetTodoItem(long id) - { - var todoItem = await _context.TodoItems.FindAsync(id); - - if (todoItem == null) - { - return NotFound(); - } - - return ItemToDTO(todoItem); - } - - [HttpPut("{id}")] - public async Task UpdateTodoItem(long id, TodoItemDTO todoItemDTO) - { - if (id != todoItemDTO.Id) - { - return BadRequest(); - } - - var todoItem = await _context.TodoItems.FindAsync(id); - if (todoItem == null) - { - return NotFound(); - } - - todoItem.Name = todoItemDTO.Name; - todoItem.IsComplete = todoItemDTO.IsComplete; - - try - { - await _context.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) when (!TodoItemExists(id)) - { - return NotFound(); - } - - return NoContent(); - } - - [HttpPost] - public async Task> CreateTodoItem(TodoItemDTO todoItemDTO) - { - var todoItem = new TodoItem - { - IsComplete = todoItemDTO.IsComplete, - Name = todoItemDTO.Name - }; - - _context.TodoItems.Add(todoItem); - await _context.SaveChangesAsync(); - - return CreatedAtAction( - nameof(GetTodoItem), - new { id = todoItem.Id }, - ItemToDTO(todoItem)); - } - - [HttpDelete("{id}")] - public async Task DeleteTodoItem(long id) - { - var todoItem = await _context.TodoItems.FindAsync(id); - - if (todoItem == null) - { - return NotFound(); - } - - _context.TodoItems.Remove(todoItem); - await _context.SaveChangesAsync(); - - return NoContent(); - } - - private bool TodoItemExists(long id) => - _context.TodoItems.Any(e => e.Id == id); - - private static TodoItemDTO ItemToDTO(TodoItem todoItem) => - new TodoItemDTO - { - Id = todoItem.Id, - Name = todoItem.Name, - IsComplete = todoItem.IsComplete - }; - } -} diff --git a/Models/TodoContext.cs b/Models/TodoContext.cs deleted file mode 100644 index 6e59e363..00000000 --- a/Models/TodoContext.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace TodoApi.Models -{ - public class TodoContext : DbContext - { - public TodoContext(DbContextOptions options) - : base(options) - { - } - - public DbSet TodoItems { get; set; } - } -} \ No newline at end of file diff --git a/Models/TodoItem.cs b/Models/TodoItem.cs deleted file mode 100644 index 1f6e5465..00000000 --- a/Models/TodoItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace TodoApi.Models -{ - #region snippet - public class TodoItem - { - public long Id { get; set; } - public string Name { get; set; } - public bool IsComplete { get; set; } - public string Secret { get; set; } - } - #endregion -} \ No newline at end of file diff --git a/TodoApiDTO.sln b/TodoApiDTO.sln index e49c182b..05bdc857 100644 --- a/TodoApiDTO.sln +++ b/TodoApiDTO.sln @@ -1,9 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30002.166 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32602.215 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodoApiDTO", "TodoApiDTO.csproj", "{623124F9-F5BA-42DD-BC26-A1720774229C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TodoApiDTO", "VeletechToDoAPI\TodoApiDTO.csproj", "{F920CF77-6BD6-4DCB-A404-175B43E18CB1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoCore", "TodoCore\TodoCore.csproj", "{1F48623E-0BE5-4F33-9DA9-F0790EC2CD4A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoInfrastructure", "TodoInfrastructure\TodoInfrastructure.csproj", "{6F3C44A1-A612-474D-BDA9-5CD1167D77AA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApplication", "TodoApplication\TodoApplication.csproj", "{45A28653-AC55-4F6D-B0B5-06DD69E6D563}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,10 +17,22 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {623124F9-F5BA-42DD-BC26-A1720774229C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {623124F9-F5BA-42DD-BC26-A1720774229C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {623124F9-F5BA-42DD-BC26-A1720774229C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {623124F9-F5BA-42DD-BC26-A1720774229C}.Release|Any CPU.Build.0 = Release|Any CPU + {F920CF77-6BD6-4DCB-A404-175B43E18CB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F920CF77-6BD6-4DCB-A404-175B43E18CB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F920CF77-6BD6-4DCB-A404-175B43E18CB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F920CF77-6BD6-4DCB-A404-175B43E18CB1}.Release|Any CPU.Build.0 = Release|Any CPU + {1F48623E-0BE5-4F33-9DA9-F0790EC2CD4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F48623E-0BE5-4F33-9DA9-F0790EC2CD4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F48623E-0BE5-4F33-9DA9-F0790EC2CD4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F48623E-0BE5-4F33-9DA9-F0790EC2CD4A}.Release|Any CPU.Build.0 = Release|Any CPU + {6F3C44A1-A612-474D-BDA9-5CD1167D77AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F3C44A1-A612-474D-BDA9-5CD1167D77AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F3C44A1-A612-474D-BDA9-5CD1167D77AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F3C44A1-A612-474D-BDA9-5CD1167D77AA}.Release|Any CPU.Build.0 = Release|Any CPU + {45A28653-AC55-4F6D-B0B5-06DD69E6D563}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45A28653-AC55-4F6D-B0B5-06DD69E6D563}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45A28653-AC55-4F6D-B0B5-06DD69E6D563}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45A28653-AC55-4F6D-B0B5-06DD69E6D563}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TodoApplication/ApplicationCollectionExtension.cs b/TodoApplication/ApplicationCollectionExtension.cs new file mode 100644 index 00000000..71a09560 --- /dev/null +++ b/TodoApplication/ApplicationCollectionExtension.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using TodoApplication.Services; +using TodoCore.Services; + +namespace TodoApplication +{ + public static class ApplicationCollectionExtension + { + public static IServiceCollection AddApplication(this IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + return services; + } + } +} diff --git a/TodoApplication/Services/AddTodoItemService.cs b/TodoApplication/Services/AddTodoItemService.cs new file mode 100644 index 00000000..d649541a --- /dev/null +++ b/TodoApplication/Services/AddTodoItemService.cs @@ -0,0 +1,27 @@ +using AutoMapper; +using System.Threading.Tasks; +using TodoCore.Data.Entities; +using TodoCore.Data.Interfaces; +using TodoCore.DTOs; +using TodoCore.Services; + +namespace TodoApplication.Services +{ + public class AddTodoItemService : IAddTodoItemService + { + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + public AddTodoItemService(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public Task AddTodoItemAsync(TodoItemDTO todoItemDTO) + { + var entity = _mapper.Map(todoItemDTO); + _unitOfWork.TodoItemReposytory.Add(entity); + return _unitOfWork.SaveChangesAsync(); + } + } +} diff --git a/TodoApplication/Services/DeleteTodoItemService.cs b/TodoApplication/Services/DeleteTodoItemService.cs new file mode 100644 index 00000000..5e81688d --- /dev/null +++ b/TodoApplication/Services/DeleteTodoItemService.cs @@ -0,0 +1,49 @@ +using AutoMapper; +using System; +using System.Threading.Tasks; +using TodoCore.Data.Entities; +using TodoCore.Data.Interfaces; +using TodoCore.DTOs; +using TodoCore.Exceptions; +using TodoCore.Services; + +namespace TodoApplication.Services +{ + public class DeleteTodoItemService : IDeleteTodoItemService + { + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public DeleteTodoItemService(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task DeleteTodoItemAsync(long id) + { + TodoItem todoItem; + try + { + todoItem = await _unitOfWork.TodoItemReposytory.GetByIdAsync(id); + } + catch(EntityNotFoundException ex) + { + throw ex; + } + using var transaction = _unitOfWork.StartTransation(); + try + { + _unitOfWork.TodoItemReposytory.Delete(todoItem); + await _unitOfWork.SaveChangesAsync(); + transaction.Commit(); + return _mapper.Map(todoItem); + } + catch(Exception ex) + { + transaction.Rollback(); + throw new SomethingWentWrongException("Something went wrong while deleting todo item"); + } + } + } +} diff --git a/TodoApplication/Services/GetTodoItemService.cs b/TodoApplication/Services/GetTodoItemService.cs new file mode 100644 index 00000000..b4803f6b --- /dev/null +++ b/TodoApplication/Services/GetTodoItemService.cs @@ -0,0 +1,35 @@ +using AutoMapper; +using System.Threading.Tasks; +using TodoCore.Data.Entities; +using TodoCore.Data.Interfaces; +using TodoCore.DTOs; +using TodoCore.Exceptions; +using TodoCore.Services; + +namespace TodoApplication.Services +{ + internal class GetTodoItemService : IGetTodoItemService + { + private readonly ITodoItemReposytory _todoItemReposytory; + private readonly IMapper _mapper; + + public GetTodoItemService(ITodoItemReposytory todoItemReposytory, IMapper mapper) + { + _todoItemReposytory = todoItemReposytory; + _mapper = mapper; + } + + public async Task GetTodoItemAsync(long id) + { + try + { + var entity = await _todoItemReposytory.GetByIdAsync(id); + return _mapper.Map(entity); + } + catch(EntityNotFoundException ex) + { + throw ex; + } + } + } +} diff --git a/TodoApplication/Services/GetTodoItemsService.cs b/TodoApplication/Services/GetTodoItemsService.cs new file mode 100644 index 00000000..9efbb3e6 --- /dev/null +++ b/TodoApplication/Services/GetTodoItemsService.cs @@ -0,0 +1,26 @@ +using AutoMapper; +using System.Collections.Generic; +using System.Threading.Tasks; +using TodoCore.Data.Interfaces; +using TodoCore.DTOs; +using TodoCore.Services; + +namespace TodoApplication.Services +{ + public class GetTodoItemsService : IGetTodoItemsService + { + private readonly ITodoItemReposytory _todoItemReposytory; + private readonly IMapper _mapper; + + public GetTodoItemsService(ITodoItemReposytory todoItemReposytory, IMapper mapper) + { + _todoItemReposytory = todoItemReposytory; + _mapper = mapper; + } + public async Task> GetTodoItemsAsync() + { + var todoItems = await _todoItemReposytory.GetAllAsync(); + return _mapper.Map>(todoItems); + } + } +} diff --git a/TodoApplication/Services/UpdateTodoItemService.cs b/TodoApplication/Services/UpdateTodoItemService.cs new file mode 100644 index 00000000..81c14ac1 --- /dev/null +++ b/TodoApplication/Services/UpdateTodoItemService.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading.Tasks; +using TodoCore.Data.Entities; +using TodoCore.Data.Interfaces; +using TodoCore.DTOs; +using TodoCore.Exceptions; +using TodoCore.Services; + +namespace TodoApplication.Services +{ + public class UpdateTodoItemService : IUpdateTodoItemService + { + private readonly IUnitOfWork _unitOfWork; + + public UpdateTodoItemService(IUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public async Task UpdateTodoItemAsync(TodoItemDTO itemDTO) + { + TodoItem todoItem; + try + { + todoItem = await _unitOfWork.TodoItemReposytory.GetByIdAsync(itemDTO.Id); + } + catch(EntityNotFoundException ex) + { + throw ex; + } + todoItem.Name = itemDTO.Name; + todoItem.IsComplete = itemDTO.IsComplete; + using var transaction = _unitOfWork.StartTransation(); + try + { + _unitOfWork.TodoItemReposytory.Update(todoItem); + await _unitOfWork.SaveChangesAsync(); + transaction.Commit(); + return itemDTO; + } + catch(Exception ex) + { + transaction.Rollback(); + throw new SomethingWentWrongException("Something went wrong wile updating item"); + } + + } + } +} diff --git a/TodoApplication/TodoApplication.csproj b/TodoApplication/TodoApplication.csproj new file mode 100644 index 00000000..d708055f --- /dev/null +++ b/TodoApplication/TodoApplication.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + diff --git a/Models/TodoItemDTO.cs b/TodoCore/DTOs/TodoItemDTO.cs similarity index 53% rename from Models/TodoItemDTO.cs rename to TodoCore/DTOs/TodoItemDTO.cs index e66a500a..9d5f99c6 100644 --- a/Models/TodoItemDTO.cs +++ b/TodoCore/DTOs/TodoItemDTO.cs @@ -1,11 +1,9 @@ -namespace TodoApi.Models +namespace TodoCore.DTOs { - #region snippet public class TodoItemDTO { public long Id { get; set; } public string Name { get; set; } - public bool IsComplete { get; set; } + public bool IsComplete { get; set; } } - #endregion } diff --git a/TodoCore/Data/Common/BaseEntity.cs b/TodoCore/Data/Common/BaseEntity.cs new file mode 100644 index 00000000..1bd8f149 --- /dev/null +++ b/TodoCore/Data/Common/BaseEntity.cs @@ -0,0 +1,7 @@ +namespace TodoCore.Data.Common +{ + public class BaseEntity + { + public long Id { get; set; } + } +} diff --git a/TodoCore/Data/Entities/TodoItem.cs b/TodoCore/Data/Entities/TodoItem.cs new file mode 100644 index 00000000..2cfe2771 --- /dev/null +++ b/TodoCore/Data/Entities/TodoItem.cs @@ -0,0 +1,10 @@ +using TodoCore.Data.Common; + +namespace TodoCore.Data.Entities +{ + public class TodoItem : BaseEntity + { + public string Name { get; set; } + public bool IsComplete { get; set; } + } +} diff --git a/TodoCore/Data/Interfaces/IReposytory.cs b/TodoCore/Data/Interfaces/IReposytory.cs new file mode 100644 index 00000000..64851b3a --- /dev/null +++ b/TodoCore/Data/Interfaces/IReposytory.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using TodoCore.Data.Common; + +namespace TodoCore.Data.Interfaces +{ + public interface IReposytory where T : BaseEntity + { + Task> GetAllAsync(); + Task GetByIdAsync(long id); + Task IsExistAsync(long id); + void Add(T entity); + T Update(T entity); + T Delete(T entity); + } +} diff --git a/TodoCore/Data/Interfaces/ITodoItemReposytory.cs b/TodoCore/Data/Interfaces/ITodoItemReposytory.cs new file mode 100644 index 00000000..54396c89 --- /dev/null +++ b/TodoCore/Data/Interfaces/ITodoItemReposytory.cs @@ -0,0 +1,9 @@ +using TodoCore.Data.Common; +using TodoCore.Data.Entities; + +namespace TodoCore.Data.Interfaces +{ + public interface ITodoItemReposytory : IReposytory + { + } +} diff --git a/TodoCore/Data/Interfaces/IUnitOfWork.cs b/TodoCore/Data/Interfaces/IUnitOfWork.cs new file mode 100644 index 00000000..4a0806a6 --- /dev/null +++ b/TodoCore/Data/Interfaces/IUnitOfWork.cs @@ -0,0 +1,12 @@ +using System.Data; +using System.Threading.Tasks; + +namespace TodoCore.Data.Interfaces +{ + public interface IUnitOfWork + { + public ITodoItemReposytory TodoItemReposytory { get; } + public Task SaveChangesAsync(); + public IDbTransaction StartTransation(); + } +} diff --git a/TodoCore/Exceptions/EntityNotFoundException.cs b/TodoCore/Exceptions/EntityNotFoundException.cs new file mode 100644 index 00000000..433eef8c --- /dev/null +++ b/TodoCore/Exceptions/EntityNotFoundException.cs @@ -0,0 +1,13 @@ +using System; +using TodoCore.Data.Common; + +namespace TodoCore.Exceptions +{ + public class EntityNotFoundException : Exception + where TEnt : BaseEntity + { + public EntityNotFoundException(string message) : base($"{typeof(TEnt).Name} {message}") + { + } + } +} diff --git a/TodoCore/Exceptions/SomethingWentWrongException.cs b/TodoCore/Exceptions/SomethingWentWrongException.cs new file mode 100644 index 00000000..1f68395a --- /dev/null +++ b/TodoCore/Exceptions/SomethingWentWrongException.cs @@ -0,0 +1,9 @@ +using System; + +namespace TodoCore.Exceptions +{ + public class SomethingWentWrongException : Exception + { + public SomethingWentWrongException(string message) : base(message) { } + } +} diff --git a/TodoCore/Services/IAddTodoItemService.cs b/TodoCore/Services/IAddTodoItemService.cs new file mode 100644 index 00000000..4b02dc95 --- /dev/null +++ b/TodoCore/Services/IAddTodoItemService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using TodoCore.DTOs; + +namespace TodoCore.Services +{ + public interface IAddTodoItemService + { + Task AddTodoItemAsync(TodoItemDTO todoItemDTO); + } +} diff --git a/TodoCore/Services/IDeleteTodoItemService.cs b/TodoCore/Services/IDeleteTodoItemService.cs new file mode 100644 index 00000000..900db708 --- /dev/null +++ b/TodoCore/Services/IDeleteTodoItemService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using TodoCore.DTOs; + +namespace TodoCore.Services +{ + public interface IDeleteTodoItemService + { + Task DeleteTodoItemAsync(long id); + } +} diff --git a/TodoCore/Services/IGetTodoItemService.cs b/TodoCore/Services/IGetTodoItemService.cs new file mode 100644 index 00000000..fc34cf3b --- /dev/null +++ b/TodoCore/Services/IGetTodoItemService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using TodoCore.DTOs; + +namespace TodoCore.Services +{ + public interface IGetTodoItemService + { + Task GetTodoItemAsync(long id); + } +} diff --git a/TodoCore/Services/IGetTodoItemsService.cs b/TodoCore/Services/IGetTodoItemsService.cs new file mode 100644 index 00000000..9b575d1f --- /dev/null +++ b/TodoCore/Services/IGetTodoItemsService.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using TodoCore.DTOs; + +namespace TodoCore.Services +{ + public interface IGetTodoItemsService + { + Task> GetTodoItemsAsync(); + } +} diff --git a/TodoCore/Services/IUpdateTodoItemService.cs b/TodoCore/Services/IUpdateTodoItemService.cs new file mode 100644 index 00000000..1be5984a --- /dev/null +++ b/TodoCore/Services/IUpdateTodoItemService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using TodoCore.DTOs; + +namespace TodoCore.Services +{ + public interface IUpdateTodoItemService + { + Task UpdateTodoItemAsync(TodoItemDTO itemDTO); + } +} diff --git a/TodoCore/TodoCore.csproj b/TodoCore/TodoCore.csproj new file mode 100644 index 00000000..cb631906 --- /dev/null +++ b/TodoCore/TodoCore.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/TodoInfrastructure/DataAccess/ApplicationDbContext.cs b/TodoInfrastructure/DataAccess/ApplicationDbContext.cs new file mode 100644 index 00000000..8f97f673 --- /dev/null +++ b/TodoInfrastructure/DataAccess/ApplicationDbContext.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using TodoCore.Data.Entities; + +namespace TodoInfrastructure.DataAccess +{ + public class ApplicationDbContext : DbContext + { + public DbSet TodoItems { get; set; } + } +} diff --git a/TodoInfrastructure/DataAccess/Repositories/Reposytory.cs b/TodoInfrastructure/DataAccess/Repositories/Reposytory.cs new file mode 100644 index 00000000..c52b4570 --- /dev/null +++ b/TodoInfrastructure/DataAccess/Repositories/Reposytory.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading.Tasks; +using TodoCore.Data.Common; +using TodoCore.Data.Interfaces; +using TodoCore.Exceptions; + +namespace TodoInfrastructure.DataAccess.Repositories +{ + public class Reposytory : IReposytory + where T : BaseEntity + { + private readonly ApplicationDbContext _context; + + public Reposytory(ApplicationDbContext context) + { + _context = context; + } + + public void Add(T entity) + { + _context.Set().Add(entity); + } + + public T Delete(T entity) + { + _context.Set().Remove(entity); + return entity; + } + + public Task> GetAllAsync() + { + return _context.Set().ToListAsync(); + } + + public async Task GetByIdAsync(long id) + { + var entity = await _context.Set().SingleOrDefaultAsync(e => e.Id == id); + if (entity == null) + throw new EntityNotFoundException("entity with id {id} wasn't found"); + return entity; + } + + public async Task IsExistAsync(long id) + { + var entity = await _context.Set().SingleOrDefaultAsync(e => e.Id == id); + return entity != null; + } + + public T Update(T entity) + { + _context.Set().Update(entity); + return entity; + } + } +} diff --git a/TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs b/TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs new file mode 100644 index 00000000..ee056110 --- /dev/null +++ b/TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs @@ -0,0 +1,10 @@ +using TodoCore.Data.Entities; +using TodoCore.Data.Interfaces; + +namespace TodoInfrastructure.DataAccess.Repositories +{ + public class TodoItemReposytory : Reposytory, ITodoItemReposytory + { + public TodoItemReposytory(ApplicationDbContext context) : base(context) { } + } +} diff --git a/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs b/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs new file mode 100644 index 00000000..8ade8e7a --- /dev/null +++ b/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; +using TodoCore.Data.Interfaces; + +namespace TodoInfrastructure.DataAccess.UOW +{ + public class UnitOfWork : IUnitOfWork + { + private readonly ApplicationDbContext _context; + private readonly ITodoItemReposytory _todoItemReposytory; + + public ITodoItemReposytory TodoItemReposytory => _todoItemReposytory; + public UnitOfWork(ApplicationDbContext context, ITodoItemReposytory todoItemReposytory) + { + _context = context; + _todoItemReposytory = todoItemReposytory; + } + + public Task SaveChangesAsync() + { + return _context.SaveChangesAsync(); + } + + public IDbTransaction StartTransation() + { + var transaction = _context.Database.BeginTransaction(); + return transaction.GetDbTransaction(); + } + } +} diff --git a/TodoInfrastructure/InfrastructureCollectionExtension.cs b/TodoInfrastructure/InfrastructureCollectionExtension.cs new file mode 100644 index 00000000..5002f7c8 --- /dev/null +++ b/TodoInfrastructure/InfrastructureCollectionExtension.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using TodoCore.Data.Interfaces; +using TodoInfrastructure.DataAccess; +using TodoInfrastructure.DataAccess.Repositories; +using TodoInfrastructure.DataAccess.UOW; + +namespace TodoInfrastructure +{ + public static class InfrastructureCollectionExtension + { + public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient(); + services.AddTransient(); + services.AddDbContext(options => + { + options.UseSqlServer(configuration.GetConnectionString(nameof(ApplicationDbContext))); + }); + return services; + + } + } +} diff --git a/TodoInfrastructure/TodoInfrastructure.csproj b/TodoInfrastructure/TodoInfrastructure.csproj new file mode 100644 index 00000000..c22c0367 --- /dev/null +++ b/TodoInfrastructure/TodoInfrastructure.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/VeletechToDoAPI/Controllers/TodoItemsController.cs b/VeletechToDoAPI/Controllers/TodoItemsController.cs new file mode 100644 index 00000000..4e90e4d2 --- /dev/null +++ b/VeletechToDoAPI/Controllers/TodoItemsController.cs @@ -0,0 +1,95 @@ +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Threading.Tasks; +using TodoCore.Data.Entities; +using TodoCore.DTOs; +using TodoCore.Exceptions; +using TodoCore.Services; + +namespace TodoApi.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TodoItemsController : ControllerBase + { + private readonly IAddTodoItemService _addTodoItemService; + private readonly IGetTodoItemService _getTodoItemService; + private readonly IGetTodoItemsService _getTodoItemsService; + private readonly IUpdateTodoItemService _updateTodoItemService; + private readonly IDeleteTodoItemService _deleteTodoItemService; + + public TodoItemsController(IAddTodoItemService addTodoItemService, IGetTodoItemService getTodoItemService, IGetTodoItemsService getTodoItemsService, + IUpdateTodoItemService updateTodoItemService, IDeleteTodoItemService deleteTodoItemService) + { + _addTodoItemService = addTodoItemService; + _getTodoItemService = getTodoItemService; + _getTodoItemsService = getTodoItemsService; + _updateTodoItemService = updateTodoItemService; + _deleteTodoItemService = deleteTodoItemService; + } + + [HttpGet] + public async Task>> GetTodoItems() + { + var result = await _getTodoItemsService.GetTodoItemsAsync(); + return Ok(result); + } + + [HttpGet("{id}")] + public async Task> GetTodoItem(long id) + { + try + { + var result = await _getTodoItemService.GetTodoItemAsync(id); + return Ok(result); + } + catch(EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + } + + [HttpPut("{id}")] + public async Task UpdateTodoItem(long id, TodoItemDTO todoItemDTO) + { + try + { + var result = await _updateTodoItemService.UpdateTodoItemAsync(todoItemDTO); + return Ok(result); + } + catch(EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch(SomethingWentWrongException ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPost] + public async Task> CreateTodoItem(TodoItemDTO todoItemDTO) + { + await _addTodoItemService.AddTodoItemAsync(todoItemDTO); + return Ok(todoItemDTO); + } + + [HttpDelete("{id}")] + public async Task DeleteTodoItem(long id) + { + try + { + var result = await _deleteTodoItemService.DeleteTodoItemAsync(id); + return Ok(); + } + catch (EntityNotFoundException) + { + return NotFound(); + } + catch (SomethingWentWrongException) + { + return StatusCode(500); + } + } + } +} diff --git a/Program.cs b/VeletechToDoAPI/Program.cs similarity index 100% rename from Program.cs rename to VeletechToDoAPI/Program.cs diff --git a/Properties/launchSettings.json b/VeletechToDoAPI/Properties/launchSettings.json similarity index 100% rename from Properties/launchSettings.json rename to VeletechToDoAPI/Properties/launchSettings.json diff --git a/README.md b/VeletechToDoAPI/README.md similarity index 100% rename from README.md rename to VeletechToDoAPI/README.md diff --git a/Startup.cs b/VeletechToDoAPI/Startup.cs similarity index 75% rename from Startup.cs rename to VeletechToDoAPI/Startup.cs index bbfbc83d..be255038 100644 --- a/Startup.cs +++ b/VeletechToDoAPI/Startup.cs @@ -10,8 +10,9 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using TodoApi.Models; +using Microsoft.OpenApi.Models; +using TodoApplication; +using TodoInfrastructure; namespace TodoApi { @@ -27,9 +28,18 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddDbContext(opt => - opt.UseInMemoryDatabase("TodoList")); + services.AddInfrastructure(Configuration); + services.AddApplication(); services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo + { + Title = "TodoItemsAPI", + Version = "v1", + Description = "TodoItems API" + }); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/TodoApiDTO.csproj b/VeletechToDoAPI/TodoApiDTO.csproj similarity index 64% rename from TodoApiDTO.csproj rename to VeletechToDoAPI/TodoApiDTO.csproj index bba6f6af..be37a5b9 100644 --- a/TodoApiDTO.csproj +++ b/VeletechToDoAPI/TodoApiDTO.csproj @@ -10,8 +10,15 @@ all - + + + + + + + + diff --git a/appsettings.Development.json b/VeletechToDoAPI/appsettings.Development.json similarity index 100% rename from appsettings.Development.json rename to VeletechToDoAPI/appsettings.Development.json diff --git a/appsettings.json b/VeletechToDoAPI/appsettings.json similarity index 100% rename from appsettings.json rename to VeletechToDoAPI/appsettings.json From ade188818f8e14fdec3e81ab29290020bb0c143d Mon Sep 17 00:00:00 2001 From: NikitaLyubimov <55978147+m11s1ma@users.noreply.github.com> Date: Fri, 28 Apr 2023 03:12:10 +0300 Subject: [PATCH 2/2] added logger and few fixes --- .../ApplicationCollectionExtension.cs | 3 ++ .../AutoMapperProfiles/TodoItemProfile.cs | 16 +++++++ .../Services/GetTodoItemService.cs | 4 +- .../Services/GetTodoItemsService.cs | 4 +- .../Services/UpdateTodoItemService.cs | 5 +-- TodoApplication/TodoApplication.csproj | 3 +- TodoCore/DTOs/UpdateTodoItemDTO.cs | 8 ++++ TodoCore/Data/Common/BaseEntity.cs | 7 ++- TodoCore/Data/Entities/TodoItem.cs | 5 ++- .../{IReposytory.cs => IRepository.cs} | 2 +- ...emReposytory.cs => ITodoItemRepository.cs} | 2 +- TodoCore/Data/Interfaces/IUnitOfWork.cs | 2 +- TodoCore/Services/IUpdateTodoItemService.cs | 2 +- .../DataAccess/ApplicationDbContext.cs | 3 +- ...0230427220746_InitialMigration.Designer.cs | 44 +++++++++++++++++++ .../20230427220746_InitialMigration.cs | 30 +++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 42 ++++++++++++++++++ .../{Reposytory.cs => Repository.cs} | 6 +-- ...temReposytory.cs => TodoItemRepository.cs} | 4 +- .../DataAccess/UOW/UnitOfWork.cs | 6 +-- .../InfrastructureCollectionExtension.cs | 2 +- .../Logger/SerilogConfiguration.cs | 20 +++++++++ TodoInfrastructure/TodoInfrastructure.csproj | 8 ++++ .../Controllers/TodoItemsController.cs | 18 +++++--- VeletechToDoAPI/Program.cs | 8 +--- VeletechToDoAPI/Startup.cs | 2 + VeletechToDoAPI/appsettings.Development.json | 3 ++ 27 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 TodoApplication/AutoMapperProfiles/TodoItemProfile.cs create mode 100644 TodoCore/DTOs/UpdateTodoItemDTO.cs rename TodoCore/Data/Interfaces/{IReposytory.cs => IRepository.cs} (85%) rename TodoCore/Data/Interfaces/{ITodoItemReposytory.cs => ITodoItemRepository.cs} (64%) create mode 100644 TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.Designer.cs create mode 100644 TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.cs create mode 100644 TodoInfrastructure/DataAccess/Migrations/ApplicationDbContextModelSnapshot.cs rename TodoInfrastructure/DataAccess/Repositories/{Reposytory.cs => Repository.cs} (86%) rename TodoInfrastructure/DataAccess/Repositories/{TodoItemReposytory.cs => TodoItemRepository.cs} (51%) create mode 100644 TodoInfrastructure/Logger/SerilogConfiguration.cs diff --git a/TodoApplication/ApplicationCollectionExtension.cs b/TodoApplication/ApplicationCollectionExtension.cs index 71a09560..ae13e375 100644 --- a/TodoApplication/ApplicationCollectionExtension.cs +++ b/TodoApplication/ApplicationCollectionExtension.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using TodoApplication.AutoMapperProfiles; using TodoApplication.Services; using TodoCore.Services; @@ -13,6 +14,8 @@ public static IServiceCollection AddApplication(this IServiceCollection services services.AddTransient(); services.AddTransient(); services.AddTransient(); + + services.AddAutoMapper(typeof(TodoItemProfile)); return services; } } diff --git a/TodoApplication/AutoMapperProfiles/TodoItemProfile.cs b/TodoApplication/AutoMapperProfiles/TodoItemProfile.cs new file mode 100644 index 00000000..3dce1f50 --- /dev/null +++ b/TodoApplication/AutoMapperProfiles/TodoItemProfile.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using TodoCore.Data.Entities; +using TodoCore.DTOs; + +namespace TodoApplication.AutoMapperProfiles +{ + public class TodoItemProfile : Profile + { + public TodoItemProfile() + { + CreateMap(); + CreateMap(); + } + + } +} diff --git a/TodoApplication/Services/GetTodoItemService.cs b/TodoApplication/Services/GetTodoItemService.cs index b4803f6b..6a53bf22 100644 --- a/TodoApplication/Services/GetTodoItemService.cs +++ b/TodoApplication/Services/GetTodoItemService.cs @@ -10,10 +10,10 @@ namespace TodoApplication.Services { internal class GetTodoItemService : IGetTodoItemService { - private readonly ITodoItemReposytory _todoItemReposytory; + private readonly ITodoItemRepository _todoItemReposytory; private readonly IMapper _mapper; - public GetTodoItemService(ITodoItemReposytory todoItemReposytory, IMapper mapper) + public GetTodoItemService(ITodoItemRepository todoItemReposytory, IMapper mapper) { _todoItemReposytory = todoItemReposytory; _mapper = mapper; diff --git a/TodoApplication/Services/GetTodoItemsService.cs b/TodoApplication/Services/GetTodoItemsService.cs index 9efbb3e6..fa3e9950 100644 --- a/TodoApplication/Services/GetTodoItemsService.cs +++ b/TodoApplication/Services/GetTodoItemsService.cs @@ -9,10 +9,10 @@ namespace TodoApplication.Services { public class GetTodoItemsService : IGetTodoItemsService { - private readonly ITodoItemReposytory _todoItemReposytory; + private readonly ITodoItemRepository _todoItemReposytory; private readonly IMapper _mapper; - public GetTodoItemsService(ITodoItemReposytory todoItemReposytory, IMapper mapper) + public GetTodoItemsService(ITodoItemRepository todoItemReposytory, IMapper mapper) { _todoItemReposytory = todoItemReposytory; _mapper = mapper; diff --git a/TodoApplication/Services/UpdateTodoItemService.cs b/TodoApplication/Services/UpdateTodoItemService.cs index 81c14ac1..cb0ee3b5 100644 --- a/TodoApplication/Services/UpdateTodoItemService.cs +++ b/TodoApplication/Services/UpdateTodoItemService.cs @@ -17,12 +17,12 @@ public UpdateTodoItemService(IUnitOfWork unitOfWork) _unitOfWork = unitOfWork; } - public async Task UpdateTodoItemAsync(TodoItemDTO itemDTO) + public async Task UpdateTodoItemAsync(long id, UpdateTodoItemDTO itemDTO) { TodoItem todoItem; try { - todoItem = await _unitOfWork.TodoItemReposytory.GetByIdAsync(itemDTO.Id); + todoItem = await _unitOfWork.TodoItemReposytory.GetByIdAsync(id); } catch(EntityNotFoundException ex) { @@ -43,7 +43,6 @@ public async Task UpdateTodoItemAsync(TodoItemDTO itemDTO) transaction.Rollback(); throw new SomethingWentWrongException("Something went wrong wile updating item"); } - } } } diff --git a/TodoApplication/TodoApplication.csproj b/TodoApplication/TodoApplication.csproj index d708055f..178ba881 100644 --- a/TodoApplication/TodoApplication.csproj +++ b/TodoApplication/TodoApplication.csproj @@ -5,7 +5,8 @@ - + + diff --git a/TodoCore/DTOs/UpdateTodoItemDTO.cs b/TodoCore/DTOs/UpdateTodoItemDTO.cs new file mode 100644 index 00000000..5c197206 --- /dev/null +++ b/TodoCore/DTOs/UpdateTodoItemDTO.cs @@ -0,0 +1,8 @@ +namespace TodoCore.DTOs +{ + public class UpdateTodoItemDTO + { + public string Name { get; set; } + public bool IsComplete { get; set; } + } +} diff --git a/TodoCore/Data/Common/BaseEntity.cs b/TodoCore/Data/Common/BaseEntity.cs index 1bd8f149..d17bbac0 100644 --- a/TodoCore/Data/Common/BaseEntity.cs +++ b/TodoCore/Data/Common/BaseEntity.cs @@ -1,7 +1,12 @@ -namespace TodoCore.Data.Common +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace TodoCore.Data.Common { public class BaseEntity { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long Id { get; set; } } } diff --git a/TodoCore/Data/Entities/TodoItem.cs b/TodoCore/Data/Entities/TodoItem.cs index 2cfe2771..04ef6cd2 100644 --- a/TodoCore/Data/Entities/TodoItem.cs +++ b/TodoCore/Data/Entities/TodoItem.cs @@ -1,10 +1,13 @@ -using TodoCore.Data.Common; +using System.ComponentModel.DataAnnotations; +using TodoCore.Data.Common; namespace TodoCore.Data.Entities { public class TodoItem : BaseEntity { + [Required] public string Name { get; set; } + [Required] public bool IsComplete { get; set; } } } diff --git a/TodoCore/Data/Interfaces/IReposytory.cs b/TodoCore/Data/Interfaces/IRepository.cs similarity index 85% rename from TodoCore/Data/Interfaces/IReposytory.cs rename to TodoCore/Data/Interfaces/IRepository.cs index 64851b3a..7ecaf9e4 100644 --- a/TodoCore/Data/Interfaces/IReposytory.cs +++ b/TodoCore/Data/Interfaces/IRepository.cs @@ -4,7 +4,7 @@ namespace TodoCore.Data.Interfaces { - public interface IReposytory where T : BaseEntity + public interface IRepository where T : BaseEntity { Task> GetAllAsync(); Task GetByIdAsync(long id); diff --git a/TodoCore/Data/Interfaces/ITodoItemReposytory.cs b/TodoCore/Data/Interfaces/ITodoItemRepository.cs similarity index 64% rename from TodoCore/Data/Interfaces/ITodoItemReposytory.cs rename to TodoCore/Data/Interfaces/ITodoItemRepository.cs index 54396c89..7aca6892 100644 --- a/TodoCore/Data/Interfaces/ITodoItemReposytory.cs +++ b/TodoCore/Data/Interfaces/ITodoItemRepository.cs @@ -3,7 +3,7 @@ namespace TodoCore.Data.Interfaces { - public interface ITodoItemReposytory : IReposytory + public interface ITodoItemRepository : IRepository { } } diff --git a/TodoCore/Data/Interfaces/IUnitOfWork.cs b/TodoCore/Data/Interfaces/IUnitOfWork.cs index 4a0806a6..c07d8e0c 100644 --- a/TodoCore/Data/Interfaces/IUnitOfWork.cs +++ b/TodoCore/Data/Interfaces/IUnitOfWork.cs @@ -5,7 +5,7 @@ namespace TodoCore.Data.Interfaces { public interface IUnitOfWork { - public ITodoItemReposytory TodoItemReposytory { get; } + public ITodoItemRepository TodoItemReposytory { get; } public Task SaveChangesAsync(); public IDbTransaction StartTransation(); } diff --git a/TodoCore/Services/IUpdateTodoItemService.cs b/TodoCore/Services/IUpdateTodoItemService.cs index 1be5984a..cede9230 100644 --- a/TodoCore/Services/IUpdateTodoItemService.cs +++ b/TodoCore/Services/IUpdateTodoItemService.cs @@ -8,6 +8,6 @@ namespace TodoCore.Services { public interface IUpdateTodoItemService { - Task UpdateTodoItemAsync(TodoItemDTO itemDTO); + Task UpdateTodoItemAsync(long id, UpdateTodoItemDTO itemDTO); } } diff --git a/TodoInfrastructure/DataAccess/ApplicationDbContext.cs b/TodoInfrastructure/DataAccess/ApplicationDbContext.cs index 8f97f673..14961ee3 100644 --- a/TodoInfrastructure/DataAccess/ApplicationDbContext.cs +++ b/TodoInfrastructure/DataAccess/ApplicationDbContext.cs @@ -1,12 +1,11 @@ using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; using TodoCore.Data.Entities; namespace TodoInfrastructure.DataAccess { public class ApplicationDbContext : DbContext { + public ApplicationDbContext(DbContextOptions options) : base(options) { } public DbSet TodoItems { get; set; } } } diff --git a/TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.Designer.cs b/TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.Designer.cs new file mode 100644 index 00000000..0ba16424 --- /dev/null +++ b/TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.Designer.cs @@ -0,0 +1,44 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TodoInfrastructure.DataAccess; + +namespace TodoInfrastructure.DataAccess.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230427220746_InitialMigration")] + partial class InitialMigration + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.32") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("TodoCore.Data.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("IsComplete") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TodoItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.cs b/TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.cs new file mode 100644 index 00000000..45cddd0c --- /dev/null +++ b/TodoInfrastructure/DataAccess/Migrations/20230427220746_InitialMigration.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace TodoInfrastructure.DataAccess.Migrations +{ + public partial class InitialMigration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TodoItems", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(nullable: false), + IsComplete = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TodoItems", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TodoItems"); + } + } +} diff --git a/TodoInfrastructure/DataAccess/Migrations/ApplicationDbContextModelSnapshot.cs b/TodoInfrastructure/DataAccess/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 00000000..3370f7c1 --- /dev/null +++ b/TodoInfrastructure/DataAccess/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,42 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TodoInfrastructure.DataAccess; + +namespace TodoInfrastructure.DataAccess.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.32") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("TodoCore.Data.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("IsComplete") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TodoItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TodoInfrastructure/DataAccess/Repositories/Reposytory.cs b/TodoInfrastructure/DataAccess/Repositories/Repository.cs similarity index 86% rename from TodoInfrastructure/DataAccess/Repositories/Reposytory.cs rename to TodoInfrastructure/DataAccess/Repositories/Repository.cs index c52b4570..c546992d 100644 --- a/TodoInfrastructure/DataAccess/Repositories/Reposytory.cs +++ b/TodoInfrastructure/DataAccess/Repositories/Repository.cs @@ -7,12 +7,12 @@ namespace TodoInfrastructure.DataAccess.Repositories { - public class Reposytory : IReposytory + public class Repository : IRepository where T : BaseEntity { private readonly ApplicationDbContext _context; - public Reposytory(ApplicationDbContext context) + public Repository(ApplicationDbContext context) { _context = context; } @@ -37,7 +37,7 @@ public async Task GetByIdAsync(long id) { var entity = await _context.Set().SingleOrDefaultAsync(e => e.Id == id); if (entity == null) - throw new EntityNotFoundException("entity with id {id} wasn't found"); + throw new EntityNotFoundException($"entity with id {id} wasn't found"); return entity; } diff --git a/TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs b/TodoInfrastructure/DataAccess/Repositories/TodoItemRepository.cs similarity index 51% rename from TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs rename to TodoInfrastructure/DataAccess/Repositories/TodoItemRepository.cs index ee056110..8b77c46a 100644 --- a/TodoInfrastructure/DataAccess/Repositories/TodoItemReposytory.cs +++ b/TodoInfrastructure/DataAccess/Repositories/TodoItemRepository.cs @@ -3,8 +3,8 @@ namespace TodoInfrastructure.DataAccess.Repositories { - public class TodoItemReposytory : Reposytory, ITodoItemReposytory + public class TodoItemRepository : Repository, ITodoItemRepository { - public TodoItemReposytory(ApplicationDbContext context) : base(context) { } + public TodoItemRepository(ApplicationDbContext context) : base(context) { } } } diff --git a/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs b/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs index 8ade8e7a..442da051 100644 --- a/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs +++ b/TodoInfrastructure/DataAccess/UOW/UnitOfWork.cs @@ -11,10 +11,10 @@ namespace TodoInfrastructure.DataAccess.UOW public class UnitOfWork : IUnitOfWork { private readonly ApplicationDbContext _context; - private readonly ITodoItemReposytory _todoItemReposytory; + private readonly ITodoItemRepository _todoItemReposytory; - public ITodoItemReposytory TodoItemReposytory => _todoItemReposytory; - public UnitOfWork(ApplicationDbContext context, ITodoItemReposytory todoItemReposytory) + public ITodoItemRepository TodoItemReposytory => _todoItemReposytory; + public UnitOfWork(ApplicationDbContext context, ITodoItemRepository todoItemReposytory) { _context = context; _todoItemReposytory = todoItemReposytory; diff --git a/TodoInfrastructure/InfrastructureCollectionExtension.cs b/TodoInfrastructure/InfrastructureCollectionExtension.cs index 5002f7c8..f8ec0023 100644 --- a/TodoInfrastructure/InfrastructureCollectionExtension.cs +++ b/TodoInfrastructure/InfrastructureCollectionExtension.cs @@ -12,7 +12,7 @@ public static class InfrastructureCollectionExtension { public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) { - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddDbContext(options => { diff --git a/TodoInfrastructure/Logger/SerilogConfiguration.cs b/TodoInfrastructure/Logger/SerilogConfiguration.cs new file mode 100644 index 00000000..05143513 --- /dev/null +++ b/TodoInfrastructure/Logger/SerilogConfiguration.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Hosting; +using Serilog; + +namespace TodoInfrastructure.Logger +{ + public static class SerilogConfiguration + { + public static IHostBuilder ConfigureSerilog(this IHostBuilder builder) + { + var loggerConfig = new LoggerConfiguration() + .WriteTo.File("Logs/errors-log.logs", + rollingInterval: RollingInterval.Day) + .MinimumLevel.Error() + .CreateLogger(); + + builder.UseSerilog(loggerConfig); + return builder; + } + } +} diff --git a/TodoInfrastructure/TodoInfrastructure.csproj b/TodoInfrastructure/TodoInfrastructure.csproj index c22c0367..6b2b3a09 100644 --- a/TodoInfrastructure/TodoInfrastructure.csproj +++ b/TodoInfrastructure/TodoInfrastructure.csproj @@ -7,7 +7,15 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/VeletechToDoAPI/Controllers/TodoItemsController.cs b/VeletechToDoAPI/Controllers/TodoItemsController.cs index 4e90e4d2..3fa422d6 100644 --- a/VeletechToDoAPI/Controllers/TodoItemsController.cs +++ b/VeletechToDoAPI/Controllers/TodoItemsController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using Serilog; using System.Collections.Generic; using System.Threading.Tasks; using TodoCore.Data.Entities; @@ -17,15 +18,17 @@ public class TodoItemsController : ControllerBase private readonly IGetTodoItemsService _getTodoItemsService; private readonly IUpdateTodoItemService _updateTodoItemService; private readonly IDeleteTodoItemService _deleteTodoItemService; + private readonly ILogger _logger; public TodoItemsController(IAddTodoItemService addTodoItemService, IGetTodoItemService getTodoItemService, IGetTodoItemsService getTodoItemsService, - IUpdateTodoItemService updateTodoItemService, IDeleteTodoItemService deleteTodoItemService) + IUpdateTodoItemService updateTodoItemService, IDeleteTodoItemService deleteTodoItemService, ILogger logger) { _addTodoItemService = addTodoItemService; _getTodoItemService = getTodoItemService; _getTodoItemsService = getTodoItemsService; _updateTodoItemService = updateTodoItemService; _deleteTodoItemService = deleteTodoItemService; + _logger = logger; } [HttpGet] @@ -45,24 +48,27 @@ public async Task> GetTodoItem(long id) } catch(EntityNotFoundException ex) { + _logger.Error(ex.Message); return NotFound(ex.Message); } } [HttpPut("{id}")] - public async Task UpdateTodoItem(long id, TodoItemDTO todoItemDTO) + public async Task UpdateTodoItem(long id, UpdateTodoItemDTO todoItemDTO) { try { - var result = await _updateTodoItemService.UpdateTodoItemAsync(todoItemDTO); + var result = await _updateTodoItemService.UpdateTodoItemAsync(id, todoItemDTO); return Ok(result); } catch(EntityNotFoundException ex) { + _logger.Error(ex.Message); return NotFound(ex.Message); } catch(SomethingWentWrongException ex) { + _logger.Error(ex.Message); return StatusCode(500, ex.Message); } } @@ -82,12 +88,14 @@ public async Task DeleteTodoItem(long id) var result = await _deleteTodoItemService.DeleteTodoItemAsync(id); return Ok(); } - catch (EntityNotFoundException) + catch (EntityNotFoundException ex) { + _logger.Error(ex.Message); return NotFound(); } - catch (SomethingWentWrongException) + catch (SomethingWentWrongException ex) { + _logger.Error(ex.Message); return StatusCode(500); } } diff --git a/VeletechToDoAPI/Program.cs b/VeletechToDoAPI/Program.cs index b27ac16a..83cb4ce4 100644 --- a/VeletechToDoAPI/Program.cs +++ b/VeletechToDoAPI/Program.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using TodoInfrastructure.Logger; namespace TodoApi { @@ -18,6 +13,7 @@ public static void Main(string[] args) public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .ConfigureSerilog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); diff --git a/VeletechToDoAPI/Startup.cs b/VeletechToDoAPI/Startup.cs index be255038..446c45ed 100644 --- a/VeletechToDoAPI/Startup.cs +++ b/VeletechToDoAPI/Startup.cs @@ -48,6 +48,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoItems API")); } app.UseHttpsRedirection(); diff --git a/VeletechToDoAPI/appsettings.Development.json b/VeletechToDoAPI/appsettings.Development.json index 8983e0fc..118df045 100644 --- a/VeletechToDoAPI/appsettings.Development.json +++ b/VeletechToDoAPI/appsettings.Development.json @@ -5,5 +5,8 @@ "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } + }, + "ConnectionStrings": { + "ApplicationDbContext": "ConnString" } }