From e437e06b27c608f5c72e7d2c8d5e80e4fab2832b Mon Sep 17 00:00:00 2001 From: kogo Date: Fri, 18 Aug 2023 11:26:45 +0400 Subject: [PATCH 1/9] update version from 3.1 to 7.0 and change arcitecture project --- .DS_Store | Bin 0 -> 6148 bytes .../Controllers}/TodoItemsController.cs | 0 {Models => Todo.API/Models}/TodoContext.cs | 0 {Models => Todo.API/Models}/TodoItem.cs | 0 {Models => Todo.API/Models}/TodoItemDTO.cs | 0 Program.cs => Todo.API/Program.cs | 0 .../Properties}/launchSettings.json | 0 README.md => Todo.API/README.md | 0 Startup.cs => Todo.API/Startup.cs | 0 TodoApiDTO.csproj => Todo.API/Todo.API.csproj | 11 +++++----- .../appsettings.Development.json | 0 appsettings.json => Todo.API/appsettings.json | 0 Todo.Core/Class1.cs | 5 +++++ Todo.Core/Todo.Core.csproj | 9 ++++++++ Todo.Infrastructure/Class1.cs | 5 +++++ .../Todo.Infrastructure.csproj | 9 ++++++++ Todo.Test/Class1.cs | 5 +++++ Todo.Test/Todo.Test.csproj | 9 ++++++++ TodoApiDTO.sln | 20 +++++++++++++++++- 19 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 .DS_Store rename {Controllers => Todo.API/Controllers}/TodoItemsController.cs (100%) rename {Models => Todo.API/Models}/TodoContext.cs (100%) rename {Models => Todo.API/Models}/TodoItem.cs (100%) rename {Models => Todo.API/Models}/TodoItemDTO.cs (100%) rename Program.cs => Todo.API/Program.cs (100%) rename {Properties => Todo.API/Properties}/launchSettings.json (100%) rename README.md => Todo.API/README.md (100%) rename Startup.cs => Todo.API/Startup.cs (100%) rename TodoApiDTO.csproj => Todo.API/Todo.API.csproj (70%) rename appsettings.Development.json => Todo.API/appsettings.Development.json (100%) rename appsettings.json => Todo.API/appsettings.json (100%) create mode 100644 Todo.Core/Class1.cs create mode 100644 Todo.Core/Todo.Core.csproj create mode 100644 Todo.Infrastructure/Class1.cs create mode 100644 Todo.Infrastructure/Todo.Infrastructure.csproj create mode 100644 Todo.Test/Class1.cs create mode 100644 Todo.Test/Todo.Test.csproj diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9a980d97a4ef58caeb09cc9fb47c738ad02156d2 GIT binary patch literal 6148 zcmeHKOG*SW5UtV?8r%%7T;>GA+@NRZLEH)B0i>rv7u{q)+~)+|V=mwgJcPILRjQP3 zIt$&1NEIZnlGLa3ppz64@#eW(5iN<*8! zoTK!vIs?vtGvEw313zH^ceY4%uIRlp;0!neUku3k5YPl`!=#vx4s^K$04^|{1-jG{ z;u8#O!=#862&*elUD-+uR(H$?v#Sl0qPi1X@xhk)vv}d09q~gpC$1H}cLtn+DFdfE zT+02w#V=DV@~197at54%f5reWnq9NOrtEH=ZBOpnfOd~2B6dj>2=vJ#00TKkF7u+U b2hm|y8zx1GBJW?xh literal 0 HcmV?d00001 diff --git a/Controllers/TodoItemsController.cs b/Todo.API/Controllers/TodoItemsController.cs similarity index 100% rename from Controllers/TodoItemsController.cs rename to Todo.API/Controllers/TodoItemsController.cs diff --git a/Models/TodoContext.cs b/Todo.API/Models/TodoContext.cs similarity index 100% rename from Models/TodoContext.cs rename to Todo.API/Models/TodoContext.cs diff --git a/Models/TodoItem.cs b/Todo.API/Models/TodoItem.cs similarity index 100% rename from Models/TodoItem.cs rename to Todo.API/Models/TodoItem.cs diff --git a/Models/TodoItemDTO.cs b/Todo.API/Models/TodoItemDTO.cs similarity index 100% rename from Models/TodoItemDTO.cs rename to Todo.API/Models/TodoItemDTO.cs diff --git a/Program.cs b/Todo.API/Program.cs similarity index 100% rename from Program.cs rename to Todo.API/Program.cs diff --git a/Properties/launchSettings.json b/Todo.API/Properties/launchSettings.json similarity index 100% rename from Properties/launchSettings.json rename to Todo.API/Properties/launchSettings.json diff --git a/README.md b/Todo.API/README.md similarity index 100% rename from README.md rename to Todo.API/README.md diff --git a/Startup.cs b/Todo.API/Startup.cs similarity index 100% rename from Startup.cs rename to Todo.API/Startup.cs diff --git a/TodoApiDTO.csproj b/Todo.API/Todo.API.csproj similarity index 70% rename from TodoApiDTO.csproj rename to Todo.API/Todo.API.csproj index bba6f6af..24a9082e 100644 --- a/TodoApiDTO.csproj +++ b/Todo.API/Todo.API.csproj @@ -1,17 +1,18 @@ - netcoreapp3.1 + net7.0 - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + + diff --git a/appsettings.Development.json b/Todo.API/appsettings.Development.json similarity index 100% rename from appsettings.Development.json rename to Todo.API/appsettings.Development.json diff --git a/appsettings.json b/Todo.API/appsettings.json similarity index 100% rename from appsettings.json rename to Todo.API/appsettings.json diff --git a/Todo.Core/Class1.cs b/Todo.Core/Class1.cs new file mode 100644 index 00000000..e6457b06 --- /dev/null +++ b/Todo.Core/Class1.cs @@ -0,0 +1,5 @@ +namespace Todo.Core; + +public class Class1 +{ +} \ No newline at end of file diff --git a/Todo.Core/Todo.Core.csproj b/Todo.Core/Todo.Core.csproj new file mode 100644 index 00000000..6836c680 --- /dev/null +++ b/Todo.Core/Todo.Core.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Todo.Infrastructure/Class1.cs b/Todo.Infrastructure/Class1.cs new file mode 100644 index 00000000..2aeaebd4 --- /dev/null +++ b/Todo.Infrastructure/Class1.cs @@ -0,0 +1,5 @@ +namespace Todo.Infrastructure; + +public class Class1 +{ +} \ No newline at end of file diff --git a/Todo.Infrastructure/Todo.Infrastructure.csproj b/Todo.Infrastructure/Todo.Infrastructure.csproj new file mode 100644 index 00000000..6836c680 --- /dev/null +++ b/Todo.Infrastructure/Todo.Infrastructure.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/Todo.Test/Class1.cs b/Todo.Test/Class1.cs new file mode 100644 index 00000000..b8a0bb20 --- /dev/null +++ b/Todo.Test/Class1.cs @@ -0,0 +1,5 @@ +namespace Todo.Test; + +public class Class1 +{ +} \ No newline at end of file diff --git a/Todo.Test/Todo.Test.csproj b/Todo.Test/Todo.Test.csproj new file mode 100644 index 00000000..6836c680 --- /dev/null +++ b/Todo.Test/Todo.Test.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/TodoApiDTO.sln b/TodoApiDTO.sln index e49c182b..bff3c84f 100644 --- a/TodoApiDTO.sln +++ b/TodoApiDTO.sln @@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30002.166 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}") = "Todo.API", "Todo.API\Todo.API.csproj", "{623124F9-F5BA-42DD-BC26-A1720774229C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Core", "Todo.Core\Todo.Core.csproj", "{C6089C27-0FEF-414E-957B-D1B72E5E1BBD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Infrastructure", "Todo.Infrastructure\Todo.Infrastructure.csproj", "{9A288DEC-FCDC-4639-B301-F018AC43CE06}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Test", "Todo.Test\Todo.Test.csproj", "{B74218DA-1686-45DD-873A-A05FA5DF38A7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +21,18 @@ Global {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 + {C6089C27-0FEF-414E-957B-D1B72E5E1BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C6089C27-0FEF-414E-957B-D1B72E5E1BBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6089C27-0FEF-414E-957B-D1B72E5E1BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C6089C27-0FEF-414E-957B-D1B72E5E1BBD}.Release|Any CPU.Build.0 = Release|Any CPU + {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Release|Any CPU.Build.0 = Release|Any CPU + {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From e51e4500ca076043b5f76a4daad7cbe3f10bfc9c Mon Sep 17 00:00:00 2001 From: kogo Date: Fri, 18 Aug 2023 11:32:01 +0400 Subject: [PATCH 2/9] add references in projects and add Swagger --- Todo.API/Properties/launchSettings.json | 2 ++ Todo.API/Startup.cs | 7 +++++++ Todo.API/Todo.API.csproj | 4 ++++ Todo.Core/Todo.Core.csproj | 4 ++++ Todo.Test/Todo.Test.csproj | 5 +++++ 5 files changed, 22 insertions(+) diff --git a/Todo.API/Properties/launchSettings.json b/Todo.API/Properties/launchSettings.json index 6766196a..cf5e46de 100644 --- a/Todo.API/Properties/launchSettings.json +++ b/Todo.API/Properties/launchSettings.json @@ -11,6 +11,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -18,6 +19,7 @@ "TodoApiDTO": { "commandName": "Project", "launchBrowser": true, + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/Todo.API/Startup.cs b/Todo.API/Startup.cs index bbfbc83d..fff38043 100644 --- a/Todo.API/Startup.cs +++ b/Todo.API/Startup.cs @@ -27,6 +27,7 @@ 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.AddSwaggerGen(); services.AddDbContext(opt => opt.UseInMemoryDatabase("TodoList")); services.AddControllers(); @@ -50,6 +51,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { endpoints.MapControllers(); }); + + app.UseSwagger(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Test1 Api v1"); + }); } } } diff --git a/Todo.API/Todo.API.csproj b/Todo.API/Todo.API.csproj index 24a9082e..ab348c77 100644 --- a/Todo.API/Todo.API.csproj +++ b/Todo.API/Todo.API.csproj @@ -15,5 +15,9 @@ + + + + diff --git a/Todo.Core/Todo.Core.csproj b/Todo.Core/Todo.Core.csproj index 6836c680..747495d0 100644 --- a/Todo.Core/Todo.Core.csproj +++ b/Todo.Core/Todo.Core.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/Todo.Test/Todo.Test.csproj b/Todo.Test/Todo.Test.csproj index 6836c680..28a66805 100644 --- a/Todo.Test/Todo.Test.csproj +++ b/Todo.Test/Todo.Test.csproj @@ -6,4 +6,9 @@ enable + + + + + From 57ab28c999b073668421816d8bd40feaae63ee86 Mon Sep 17 00:00:00 2001 From: kogo Date: Fri, 18 Aug 2023 11:55:43 +0400 Subject: [PATCH 3/9] add entities and entityFramework --- Todo.Core/Class1.cs | 5 ----- Todo.Core/Todo.Core.csproj | 6 ++++++ Todo.Infrastructure/DbContexts/TodoDbContext.cs | 14 ++++++++++++++ Todo.Infrastructure/Entities/TodoItem.cs | 12 ++++++++++++ Todo.Infrastructure/Todo.Infrastructure.csproj | 13 +++++++++++++ 5 files changed, 45 insertions(+), 5 deletions(-) delete mode 100644 Todo.Core/Class1.cs create mode 100644 Todo.Infrastructure/DbContexts/TodoDbContext.cs create mode 100644 Todo.Infrastructure/Entities/TodoItem.cs diff --git a/Todo.Core/Class1.cs b/Todo.Core/Class1.cs deleted file mode 100644 index e6457b06..00000000 --- a/Todo.Core/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Todo.Core; - -public class Class1 -{ -} \ No newline at end of file diff --git a/Todo.Core/Todo.Core.csproj b/Todo.Core/Todo.Core.csproj index 747495d0..c5ab1ea6 100644 --- a/Todo.Core/Todo.Core.csproj +++ b/Todo.Core/Todo.Core.csproj @@ -10,4 +10,10 @@ + + + + + + diff --git a/Todo.Infrastructure/DbContexts/TodoDbContext.cs b/Todo.Infrastructure/DbContexts/TodoDbContext.cs new file mode 100644 index 00000000..a80ec8cb --- /dev/null +++ b/Todo.Infrastructure/DbContexts/TodoDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using Todo.Infrastructure.Entities; + +namespace Todo.Infrastructure.DbContexts; + +public class TodoDbContext : DbContext +{ + public TodoDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet TodoItems { get; set; } +} \ No newline at end of file diff --git a/Todo.Infrastructure/Entities/TodoItem.cs b/Todo.Infrastructure/Entities/TodoItem.cs new file mode 100644 index 00000000..d0d50a7a --- /dev/null +++ b/Todo.Infrastructure/Entities/TodoItem.cs @@ -0,0 +1,12 @@ +namespace Todo.Infrastructure.Entities; + +public class TodoItem +{ + public long Id { get; set; } + + public string Name { get; set; } + + public bool IsComplete { get; set; } + + public string Secret { get; set; } +} \ No newline at end of file diff --git a/Todo.Infrastructure/Todo.Infrastructure.csproj b/Todo.Infrastructure/Todo.Infrastructure.csproj index 6836c680..5a85f99e 100644 --- a/Todo.Infrastructure/Todo.Infrastructure.csproj +++ b/Todo.Infrastructure/Todo.Infrastructure.csproj @@ -6,4 +6,17 @@ enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + From a497c0720662a33511bac5279422056e4b03cb05 Mon Sep 17 00:00:00 2001 From: kogo Date: Fri, 18 Aug 2023 15:47:09 +0400 Subject: [PATCH 4/9] add connection string and run first migratioin --- .DS_Store | Bin 6148 -> 6148 bytes Todo.API/Controllers/TodoItemsController.cs | 232 +++++++++--------- Todo.API/Models/TodoContext.cs | 28 +-- Todo.API/Startup.cs | 13 +- Todo.API/Todo.API.csproj | 11 +- Todo.API/appsettings.json | 5 +- .../DbContexts/TodoDbContext.cs | 1 + ...230818113629_YourMigrationName.Designer.cs | 53 ++++ .../20230818113629_YourMigrationName.cs | 36 +++ .../20230818114606_updateTable.Designer.cs | 53 ++++ .../Migrations/20230818114606_updateTable.cs | 22 ++ .../Migrations/TodoDbContextModelSnapshot.cs | 50 ++++ 12 files changed, 357 insertions(+), 147 deletions(-) create mode 100644 Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.Designer.cs create mode 100644 Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.cs create mode 100644 Todo.Infrastructure/Migrations/20230818114606_updateTable.Designer.cs create mode 100644 Todo.Infrastructure/Migrations/20230818114606_updateTable.cs create mode 100644 Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs diff --git a/.DS_Store b/.DS_Store index 9a980d97a4ef58caeb09cc9fb47c738ad02156d2..7e301461f810729529f59b068975ad43536ec92a 100644 GIT binary patch delta 55 zcmZoMXfc@J&&W10U^gS%WFE#H(wqz-4EYQxK&%I3r7{#Vl%y0V=OpFl=WIU6sKK_G Io#QV*0G~|{l>h($ delta 30 mcmZoMXfc@J&&WD4U^gS{WFE#Ho8K@>vrTN!*v!uHmmdI{LkbW8 diff --git a/Todo.API/Controllers/TodoItemsController.cs b/Todo.API/Controllers/TodoItemsController.cs index 0ef138e7..0ab875d2 100644 --- a/Todo.API/Controllers/TodoItemsController.cs +++ b/Todo.API/Controllers/TodoItemsController.cs @@ -1,116 +1,116 @@ -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 - }; - } -} +// 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/Todo.API/Models/TodoContext.cs b/Todo.API/Models/TodoContext.cs index 6e59e363..89a55330 100644 --- a/Todo.API/Models/TodoContext.cs +++ b/Todo.API/Models/TodoContext.cs @@ -1,14 +1,14 @@ -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 +// 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/Todo.API/Startup.cs b/Todo.API/Startup.cs index fff38043..f18ba1e9 100644 --- a/Todo.API/Startup.cs +++ b/Todo.API/Startup.cs @@ -1,17 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using TodoApi.Models; +using Todo.Infrastructure.DbContexts; namespace TodoApi { @@ -28,8 +21,8 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddSwaggerGen(); - services.AddDbContext(opt => - opt.UseInMemoryDatabase("TodoList")); + services.AddDbContext(opt => + opt.UseSqlServer("Sql")); services.AddControllers(); } diff --git a/Todo.API/Todo.API.csproj b/Todo.API/Todo.API.csproj index ab348c77..78250287 100644 --- a/Todo.API/Todo.API.csproj +++ b/Todo.API/Todo.API.csproj @@ -5,14 +5,13 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + diff --git a/Todo.API/appsettings.json b/Todo.API/appsettings.json index d9d9a9bf..dd34b363 100644 --- a/Todo.API/appsettings.json +++ b/Todo.API/appsettings.json @@ -6,5 +6,8 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStrings": { + "Sql": "Data Source=localhost;Initial Catalog=Account;Integrated Security=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" + } } diff --git a/Todo.Infrastructure/DbContexts/TodoDbContext.cs b/Todo.Infrastructure/DbContexts/TodoDbContext.cs index a80ec8cb..bda7f878 100644 --- a/Todo.Infrastructure/DbContexts/TodoDbContext.cs +++ b/Todo.Infrastructure/DbContexts/TodoDbContext.cs @@ -5,6 +5,7 @@ namespace Todo.Infrastructure.DbContexts; public class TodoDbContext : DbContext { + public TodoDbContext(){} public TodoDbContext(DbContextOptions options) : base(options) { diff --git a/Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.Designer.cs b/Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.Designer.cs new file mode 100644 index 00000000..0b3ba995 --- /dev/null +++ b/Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.Designer.cs @@ -0,0 +1,53 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Todo.Infrastructure.DbContexts; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + [DbContext(typeof(TodoDbContext))] + [Migration("20230818113629_YourMigrationName")] + partial class YourMigrationName + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Todo.Infrastructure.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsComplete") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Secret") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TodoItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.cs b/Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.cs new file mode 100644 index 00000000..b0c83015 --- /dev/null +++ b/Todo.Infrastructure/Migrations/20230818113629_YourMigrationName.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + /// + public partial class YourMigrationName : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TodoItems", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(max)", nullable: false), + IsComplete = table.Column(type: "bit", nullable: false), + Secret = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TodoItems", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TodoItems"); + } + } +} diff --git a/Todo.Infrastructure/Migrations/20230818114606_updateTable.Designer.cs b/Todo.Infrastructure/Migrations/20230818114606_updateTable.Designer.cs new file mode 100644 index 00000000..884c28ab --- /dev/null +++ b/Todo.Infrastructure/Migrations/20230818114606_updateTable.Designer.cs @@ -0,0 +1,53 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Todo.Infrastructure.DbContexts; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + [DbContext(typeof(TodoDbContext))] + [Migration("20230818114606_updateTable")] + partial class updateTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Todo.Infrastructure.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsComplete") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Secret") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TodoItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Todo.Infrastructure/Migrations/20230818114606_updateTable.cs b/Todo.Infrastructure/Migrations/20230818114606_updateTable.cs new file mode 100644 index 00000000..d084700e --- /dev/null +++ b/Todo.Infrastructure/Migrations/20230818114606_updateTable.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + /// + public partial class updateTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs b/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs new file mode 100644 index 00000000..31708c0d --- /dev/null +++ b/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs @@ -0,0 +1,50 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Todo.Infrastructure.DbContexts; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + [DbContext(typeof(TodoDbContext))] + partial class TodoDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Todo.Infrastructure.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsComplete") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Secret") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TodoItems"); + }); +#pragma warning restore 612, 618 + } + } +} From 86fa6905ac1c94100789937b16140b3786087588 Mon Sep 17 00:00:00 2001 From: kogo Date: Fri, 18 Aug 2023 16:24:24 +0400 Subject: [PATCH 5/9] add repository and service --- Todo.API/Controllers/TodoItemsController.cs | 239 +++++++++--------- Todo.API/Models/TodoContext.cs | 14 - Todo.API/Models/TodoItem.cs | 12 - Todo.Core/Interfaces/IItemService.cs | 16 ++ Todo.Core/Services/ItemService.cs | 40 +++ Todo.Core/Todo.Core.csproj | 2 - Todo.Infrastructure/Class1.cs | 5 - Todo.Infrastructure/Interfaces/IRepository.cs | 19 ++ .../Repositories/Repository.cs | 37 +++ .../Todo.Infrastructure.csproj | 4 - 10 files changed, 235 insertions(+), 153 deletions(-) delete mode 100644 Todo.API/Models/TodoContext.cs delete mode 100644 Todo.API/Models/TodoItem.cs create mode 100644 Todo.Core/Interfaces/IItemService.cs create mode 100644 Todo.Core/Services/ItemService.cs delete mode 100644 Todo.Infrastructure/Class1.cs create mode 100644 Todo.Infrastructure/Interfaces/IRepository.cs create mode 100644 Todo.Infrastructure/Repositories/Repository.cs diff --git a/Todo.API/Controllers/TodoItemsController.cs b/Todo.API/Controllers/TodoItemsController.cs index 0ab875d2..5a15a33d 100644 --- a/Todo.API/Controllers/TodoItemsController.cs +++ b/Todo.API/Controllers/TodoItemsController.cs @@ -1,116 +1,123 @@ -// 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 -// }; -// } -// } +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Todo.Core.Interfaces; +using Todo.Infrastructure.Entities; +using TodoApi.Models; + +namespace TodoApi.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TodoItemsController : ControllerBase + { + private readonly IItemService _itemService; + + public TodoItemsController(IItemService itemService) + { + _itemService = itemService; + } + + [HttpGet] + public async Task>> GetTodoItems() + { + var result = await _itemService.GetAll(); + + // return await result + // .Select(x => ItemToDTO(x)) + // .ToListAsync(); + } + + [HttpGet("{id}")] + public async Task> GetTodoItem(long id) + { + var item = await _itemService.Read(id); + + return ItemToDTO(item); + // 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 + }; + } +} \ No newline at end of file diff --git a/Todo.API/Models/TodoContext.cs b/Todo.API/Models/TodoContext.cs deleted file mode 100644 index 89a55330..00000000 --- a/Todo.API/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/Todo.API/Models/TodoItem.cs b/Todo.API/Models/TodoItem.cs deleted file mode 100644 index 1f6e5465..00000000 --- a/Todo.API/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/Todo.Core/Interfaces/IItemService.cs b/Todo.Core/Interfaces/IItemService.cs new file mode 100644 index 00000000..4a4e38b5 --- /dev/null +++ b/Todo.Core/Interfaces/IItemService.cs @@ -0,0 +1,16 @@ +using Todo.Infrastructure.Entities; + +namespace Todo.Core.Interfaces; + +public interface IItemService +{ + Task Create(TodoItem item); + + Task Read(long id); + + Task Update(TodoItem item); + + Task Delete(TodoItem item); + + Task> GetAll(); +} \ No newline at end of file diff --git a/Todo.Core/Services/ItemService.cs b/Todo.Core/Services/ItemService.cs new file mode 100644 index 00000000..dd5ebc26 --- /dev/null +++ b/Todo.Core/Services/ItemService.cs @@ -0,0 +1,40 @@ +using Todo.Core.Interfaces; +using Todo.Infrastructure.Entities; +using Todo.Infrastructure.Interfaces; + +namespace Todo.Core.Services; + +public class ItemService : IItemService +{ + private readonly IRepository _repository; + + public ItemService(IRepository repository) + { + _repository = repository; + } + + public async Task Create(TodoItem item) + { + await _repository.Create(item); + } + + public async Task Read(long id) + { + return await _repository.Read(x => x.Id == id); + } + + public async Task Update(TodoItem item) + { + await _repository.Update(item); + } + + public async Task Delete(TodoItem item) + { + await _repository.Delete(item); + } + + public async Task> GetAll() + { + return await _repository.GetAll(); + } +} \ No newline at end of file diff --git a/Todo.Core/Todo.Core.csproj b/Todo.Core/Todo.Core.csproj index c5ab1ea6..583ad1f0 100644 --- a/Todo.Core/Todo.Core.csproj +++ b/Todo.Core/Todo.Core.csproj @@ -11,9 +11,7 @@ - - diff --git a/Todo.Infrastructure/Class1.cs b/Todo.Infrastructure/Class1.cs deleted file mode 100644 index 2aeaebd4..00000000 --- a/Todo.Infrastructure/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Todo.Infrastructure; - -public class Class1 -{ -} \ No newline at end of file diff --git a/Todo.Infrastructure/Interfaces/IRepository.cs b/Todo.Infrastructure/Interfaces/IRepository.cs new file mode 100644 index 00000000..da2f6122 --- /dev/null +++ b/Todo.Infrastructure/Interfaces/IRepository.cs @@ -0,0 +1,19 @@ +using System.Linq.Expressions; + +namespace Todo.Infrastructure.Interfaces; + +public interface IRepository + where T : class +{ + Task Create(T entity); + + Task Read(Func condition); + + Task Update(T entity); + + Task Delete(T entity); + + Task SaveChanges(); + + Task> GetAll(); +} \ No newline at end of file diff --git a/Todo.Infrastructure/Repositories/Repository.cs b/Todo.Infrastructure/Repositories/Repository.cs new file mode 100644 index 00000000..e69a51d1 --- /dev/null +++ b/Todo.Infrastructure/Repositories/Repository.cs @@ -0,0 +1,37 @@ +using Todo.Infrastructure.Interfaces; + +namespace Todo.Infrastructure.Repositories; + +public class Repository : IRepository + where T : class +{ + public async Task Create(T entity) + { + throw new NotImplementedException(); + } + + public async Task Read(Func condition) + { + throw new NotImplementedException(); + } + + public async Task Update(T entity) + { + throw new NotImplementedException(); + } + + public async Task Delete(T entity) + { + throw new NotImplementedException(); + } + + public async Task SaveChanges() + { + throw new NotImplementedException(); + } + + public async Task> GetAll() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Todo.Infrastructure/Todo.Infrastructure.csproj b/Todo.Infrastructure/Todo.Infrastructure.csproj index 5a85f99e..7aea24f2 100644 --- a/Todo.Infrastructure/Todo.Infrastructure.csproj +++ b/Todo.Infrastructure/Todo.Infrastructure.csproj @@ -15,8 +15,4 @@ - - - - From f258a9c3f25f81860a1c4c3fc0e44f7831f21995 Mon Sep 17 00:00:00 2001 From: kogo Date: Fri, 18 Aug 2023 20:39:06 +0400 Subject: [PATCH 6/9] add logic in repository and service --- Todo.API/Controllers/TodoItemsController.cs | 246 +++++++++--------- Todo.API/Models/TodoItemDTO.cs | 11 - Todo.Core/Models/TodoItemDTO.cs | 8 + Todo.Core/Services/ItemService.cs | 9 +- Todo.Infrastructure/Interfaces/IRepository.cs | 8 +- .../Repositories/Repository.cs | 35 ++- 6 files changed, 166 insertions(+), 151 deletions(-) delete mode 100644 Todo.API/Models/TodoItemDTO.cs create mode 100644 Todo.Core/Models/TodoItemDTO.cs diff --git a/Todo.API/Controllers/TodoItemsController.cs b/Todo.API/Controllers/TodoItemsController.cs index 5a15a33d..3f135172 100644 --- a/Todo.API/Controllers/TodoItemsController.cs +++ b/Todo.API/Controllers/TodoItemsController.cs @@ -1,123 +1,123 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Todo.Core.Interfaces; -using Todo.Infrastructure.Entities; -using TodoApi.Models; - -namespace TodoApi.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class TodoItemsController : ControllerBase - { - private readonly IItemService _itemService; - - public TodoItemsController(IItemService itemService) - { - _itemService = itemService; - } - - [HttpGet] - public async Task>> GetTodoItems() - { - var result = await _itemService.GetAll(); - - // return await result - // .Select(x => ItemToDTO(x)) - // .ToListAsync(); - } - - [HttpGet("{id}")] - public async Task> GetTodoItem(long id) - { - var item = await _itemService.Read(id); - - return ItemToDTO(item); - // 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 - }; - } -} \ No newline at end of file +// using Microsoft.AspNetCore.Mvc; +// using Microsoft.EntityFrameworkCore; +// using System.Collections.Generic; +// using System.Linq; +// using System.Threading.Tasks; +// using Todo.Core.Interfaces; +// using Todo.Core.Models; +// using Todo.Infrastructure.Entities; +// +// namespace TodoApi.Controllers +// { +// [Route("api/[controller]")] +// [ApiController] +// public class TodoItemsController : ControllerBase +// { +// private readonly IItemService _itemService; +// +// public TodoItemsController(IItemService itemService) +// { +// _itemService = itemService; +// } +// +// [HttpGet] +// public async Task>> GetTodoItems() +// { +// var result = await _itemService.GetAll(); +// +// // return await result +// // .Select(x => ItemToDTO(x)) +// // .ToListAsync(); +// } +// +// [HttpGet("{id}")] +// public async Task> GetTodoItem(long id) +// { +// var item = await _itemService.Read(id); +// +// return ItemToDTO(item); +// // 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 +// }; +// } +// } \ No newline at end of file diff --git a/Todo.API/Models/TodoItemDTO.cs b/Todo.API/Models/TodoItemDTO.cs deleted file mode 100644 index e66a500a..00000000 --- a/Todo.API/Models/TodoItemDTO.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace TodoApi.Models -{ - #region snippet - public class TodoItemDTO - { - public long Id { get; set; } - public string Name { get; set; } - public bool IsComplete { get; set; } - } - #endregion -} diff --git a/Todo.Core/Models/TodoItemDTO.cs b/Todo.Core/Models/TodoItemDTO.cs new file mode 100644 index 00000000..31bcecda --- /dev/null +++ b/Todo.Core/Models/TodoItemDTO.cs @@ -0,0 +1,8 @@ +namespace Todo.Core.Models; + +public class TodoItemDTO +{ + public long Id { get; set; } + public string Name { get; set; } + public bool IsComplete { get; set; } +} \ No newline at end of file diff --git a/Todo.Core/Services/ItemService.cs b/Todo.Core/Services/ItemService.cs index dd5ebc26..dd7d2048 100644 --- a/Todo.Core/Services/ItemService.cs +++ b/Todo.Core/Services/ItemService.cs @@ -15,7 +15,8 @@ public ItemService(IRepository repository) public async Task Create(TodoItem item) { - await _repository.Create(item); + _repository.Create(item); + await _repository.SaveChanges(); } public async Task Read(long id) @@ -25,12 +26,14 @@ public async Task Read(long id) public async Task Update(TodoItem item) { - await _repository.Update(item); + _repository.Update(item); + await _repository.SaveChanges(); } public async Task Delete(TodoItem item) { - await _repository.Delete(item); + _repository.Delete(item); + await _repository.SaveChanges(); } public async Task> GetAll() diff --git a/Todo.Infrastructure/Interfaces/IRepository.cs b/Todo.Infrastructure/Interfaces/IRepository.cs index da2f6122..eb0ef14f 100644 --- a/Todo.Infrastructure/Interfaces/IRepository.cs +++ b/Todo.Infrastructure/Interfaces/IRepository.cs @@ -5,13 +5,13 @@ namespace Todo.Infrastructure.Interfaces; public interface IRepository where T : class { - Task Create(T entity); + void Create(T entity); - Task Read(Func condition); + Task Read(Expression> condition); - Task Update(T entity); + void Update(T entity); - Task Delete(T entity); + void Delete(T entity); Task SaveChanges(); diff --git a/Todo.Infrastructure/Repositories/Repository.cs b/Todo.Infrastructure/Repositories/Repository.cs index e69a51d1..c21ce6fd 100644 --- a/Todo.Infrastructure/Repositories/Repository.cs +++ b/Todo.Infrastructure/Repositories/Repository.cs @@ -1,3 +1,6 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Todo.Infrastructure.DbContexts; using Todo.Infrastructure.Interfaces; namespace Todo.Infrastructure.Repositories; @@ -5,33 +8,45 @@ namespace Todo.Infrastructure.Repositories; public class Repository : IRepository where T : class { - public async Task Create(T entity) + private readonly TodoDbContext _context; + + public Repository(TodoDbContext context) + { + _context = context; + } + + public void Create(T entity) { - throw new NotImplementedException(); + _context.Set().Add(entity); } - public async Task Read(Func condition) + public async Task Read(Expression> condition) { - throw new NotImplementedException(); + return await _context.Set() + .Where(condition) + .AsNoTracking() + .FirstOrDefaultAsync(); } - public async Task Update(T entity) + public void Update(T entity) { - throw new NotImplementedException(); + _context.Set().Update(entity); } - public async Task Delete(T entity) + public void Delete(T entity) { - throw new NotImplementedException(); + _context.Set().Remove(entity); } public async Task SaveChanges() { - throw new NotImplementedException(); + await _context.SaveChangesAsync(); } public async Task> GetAll() { - throw new NotImplementedException(); + return await _context.Set() + .AsNoTracking() + .ToListAsync(); } } \ No newline at end of file From 6293e503da6321432f2e33050ab3c7881be1836c Mon Sep 17 00:00:00 2001 From: kogo Date: Sun, 20 Aug 2023 21:51:17 +0400 Subject: [PATCH 7/9] add mapper and fix some bugs --- Todo.API/Controllers/TodoItemsController.cs | 217 ++++++++---------- Todo.API/Startup.cs | 34 ++- Todo.API/appsettings.json | 2 +- Todo.Core/Exceptions/ErrorDetails.cs | 13 ++ Todo.Core/Exceptions/ItemNotFoundException.cs | 13 ++ Todo.Core/Mappings/MappingProfile.cs | 16 ++ .../Models/{ => TodoItem}/TodoItemDTO.cs | 6 +- .../Models/TodoItem/TodoItemDTOCreate.cs | 5 + .../TodoItem/TodoItemDTOManipulation.cs | 12 + .../Models/TodoItem/TodoItemDTOUpdate.cs | 6 + Todo.Core/Todo.Core.csproj | 2 +- .../DbContexts/TodoDbContext.cs | 4 +- Todo.Infrastructure/Entities/TodoItem.cs | 6 +- .../20230820174520_updateColumn.Designer.cs | 51 ++++ .../Migrations/20230820174520_updateColumn.cs | 54 +++++ .../Migrations/TodoDbContextModelSnapshot.cs | 2 - .../Repositories/Repository.cs | 8 +- 17 files changed, 309 insertions(+), 142 deletions(-) create mode 100644 Todo.Core/Exceptions/ErrorDetails.cs create mode 100644 Todo.Core/Exceptions/ItemNotFoundException.cs create mode 100644 Todo.Core/Mappings/MappingProfile.cs rename Todo.Core/Models/{ => TodoItem}/TodoItemDTO.cs (73%) create mode 100644 Todo.Core/Models/TodoItem/TodoItemDTOCreate.cs create mode 100644 Todo.Core/Models/TodoItem/TodoItemDTOManipulation.cs create mode 100644 Todo.Core/Models/TodoItem/TodoItemDTOUpdate.cs create mode 100644 Todo.Infrastructure/Migrations/20230820174520_updateColumn.Designer.cs create mode 100644 Todo.Infrastructure/Migrations/20230820174520_updateColumn.cs diff --git a/Todo.API/Controllers/TodoItemsController.cs b/Todo.API/Controllers/TodoItemsController.cs index 3f135172..fe591b7b 100644 --- a/Todo.API/Controllers/TodoItemsController.cs +++ b/Todo.API/Controllers/TodoItemsController.cs @@ -1,123 +1,94 @@ -// using Microsoft.AspNetCore.Mvc; -// using Microsoft.EntityFrameworkCore; -// using System.Collections.Generic; -// using System.Linq; -// using System.Threading.Tasks; -// using Todo.Core.Interfaces; -// using Todo.Core.Models; -// using Todo.Infrastructure.Entities; -// -// namespace TodoApi.Controllers -// { -// [Route("api/[controller]")] -// [ApiController] -// public class TodoItemsController : ControllerBase -// { -// private readonly IItemService _itemService; -// -// public TodoItemsController(IItemService itemService) -// { -// _itemService = itemService; -// } -// -// [HttpGet] -// public async Task>> GetTodoItems() -// { -// var result = await _itemService.GetAll(); -// -// // return await result -// // .Select(x => ItemToDTO(x)) -// // .ToListAsync(); -// } -// -// [HttpGet("{id}")] -// public async Task> GetTodoItem(long id) -// { -// var item = await _itemService.Read(id); -// -// return ItemToDTO(item); -// // 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 -// }; -// } -// } \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Mvc; +using Todo.Core.Exceptions; +using Todo.Core.Interfaces; +using Todo.Core.Models; +using Todo.Core.Models.TodoItem; +using Todo.Infrastructure.Entities; + +namespace Todo.API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class TodoItemsController : ControllerBase + { + private readonly IItemService _itemService; + private readonly IMapper _mapper; + + public TodoItemsController(IItemService itemService, IMapper mapper) + { + _itemService = itemService; + _mapper = mapper; + } + + [HttpGet] + public async Task>> GetTodoItems() + { + try + { + var result = await _itemService.GetAll(); + + return Ok(_mapper.Map>(result)); + } + catch (Exception ex) + { + var errorDetails = new ErrorDetails(500, ex.Message); + return StatusCode(500, errorDetails); + } + } + + [HttpGet("{id}")] + public async Task> GetTodoItem(long id) + { + var item = await _itemService.Read(id); + + return Ok(_mapper.Map(item)); + } + + [HttpPut("{id}")] + public async Task UpdateTodoItem(long id, [FromBody] TodoItemDTOUpdate todoItemDTO) + { + var item = await TodoItemExists(id); + _mapper.Map(todoItemDTO, item); + + await _itemService.Update(item); + + return NoContent(); + } + + + [HttpPost] + public async Task> CreateTodoItem([FromBody] TodoItemDTOCreate todoItemDTO) + { + var toEntity = _mapper.Map(todoItemDTO); + await _itemService.Create(toEntity); + + var createdItem = _mapper.Map(toEntity); + + return CreatedAtRoute("TodoItemById", new { todoItemId = createdItem.Id }, createdItem); + + } + + [HttpDelete("{id}")] + public async Task DeleteTodoItem(long id) + { + var item = await TodoItemExists(id); + await _itemService.Delete(item); + + return NoContent(); + } + + private async Task TodoItemExists(long id) + { + var item = await _itemService.Read(id); + + if (item is null) + throw new Exception(); + + return item; + } + } +} \ No newline at end of file diff --git a/Todo.API/Startup.cs b/Todo.API/Startup.cs index f18ba1e9..ca47b76b 100644 --- a/Todo.API/Startup.cs +++ b/Todo.API/Startup.cs @@ -1,10 +1,17 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Todo.Core.Interfaces; +using Todo.Core.Mappings; +using Todo.Core.Services; using Todo.Infrastructure.DbContexts; +using Todo.Infrastructure.Entities; +using Todo.Infrastructure.Interfaces; +using Todo.Infrastructure.Repositories; namespace TodoApi { @@ -20,9 +27,14 @@ 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.AddTransient, Repository>(); + services.AddTransient(); services.AddSwaggerGen(); - services.AddDbContext(opt => - opt.UseSqlServer("Sql")); + services.AddAutoMapper(typeof(MappingProfile)); + + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("Sql"))); + services.AddControllers(); } @@ -43,13 +55,19 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { endpoints.MapControllers(); + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + + // You can define additional routes here + endpoints.MapControllerRoute( + name: "TodoItemById", + pattern: "api/todo/{todoItemId}", // Define your custom route pattern here + defaults: new { controller = "TodoItems", action = "GetTodoItem" }); // Controller and action names }); - + app.UseSwagger(); - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Test1 Api v1"); - }); + app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Test1 Api v1"); }); } } -} +} \ No newline at end of file diff --git a/Todo.API/appsettings.json b/Todo.API/appsettings.json index dd34b363..d35eaa4d 100644 --- a/Todo.API/appsettings.json +++ b/Todo.API/appsettings.json @@ -8,6 +8,6 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "Sql": "Data Source=localhost;Initial Catalog=Account;Integrated Security=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" + "Sql": "Server=localhost;Database=Account;User Id=sa;Password=Password.1;ApplicationIntent=ReadWrite;MultiSubnetFailover=False; TrustServerCertificate=True" } } diff --git a/Todo.Core/Exceptions/ErrorDetails.cs b/Todo.Core/Exceptions/ErrorDetails.cs new file mode 100644 index 00000000..3a537981 --- /dev/null +++ b/Todo.Core/Exceptions/ErrorDetails.cs @@ -0,0 +1,13 @@ +namespace Todo.Core.Exceptions; + +public class ErrorDetails +{ + public int StatusCode { get; set; } + public string Message { get; set; } + + public ErrorDetails(int statusCode, string message) + { + StatusCode = statusCode; + Message = message; + } +} \ No newline at end of file diff --git a/Todo.Core/Exceptions/ItemNotFoundException.cs b/Todo.Core/Exceptions/ItemNotFoundException.cs new file mode 100644 index 00000000..bbbf8205 --- /dev/null +++ b/Todo.Core/Exceptions/ItemNotFoundException.cs @@ -0,0 +1,13 @@ +namespace Todo.Core.Exceptions; + +public class ItemNotFoundException : Exception +{ + public ItemNotFoundException(string message) : base(message) + { + } + + public ErrorDetails ToErrorDetails() + { + return new ErrorDetails(404, Message); // 404 indicates "Not Found" status + } +} \ No newline at end of file diff --git a/Todo.Core/Mappings/MappingProfile.cs b/Todo.Core/Mappings/MappingProfile.cs new file mode 100644 index 00000000..e5ade854 --- /dev/null +++ b/Todo.Core/Mappings/MappingProfile.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using Todo.Core.Models; +using Todo.Core.Models.TodoItem; +using Todo.Infrastructure.Entities; + +namespace Todo.Core.Mappings; + +public class MappingProfile : Profile +{ + public MappingProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + } +} \ No newline at end of file diff --git a/Todo.Core/Models/TodoItemDTO.cs b/Todo.Core/Models/TodoItem/TodoItemDTO.cs similarity index 73% rename from Todo.Core/Models/TodoItemDTO.cs rename to Todo.Core/Models/TodoItem/TodoItemDTO.cs index 31bcecda..8297a03a 100644 --- a/Todo.Core/Models/TodoItemDTO.cs +++ b/Todo.Core/Models/TodoItem/TodoItemDTO.cs @@ -1,8 +1,10 @@ -namespace Todo.Core.Models; +namespace Todo.Core.Models.TodoItem; public class TodoItemDTO { public long Id { get; set; } + public string Name { get; set; } + public bool IsComplete { get; set; } -} \ No newline at end of file +} diff --git a/Todo.Core/Models/TodoItem/TodoItemDTOCreate.cs b/Todo.Core/Models/TodoItem/TodoItemDTOCreate.cs new file mode 100644 index 00000000..11446e94 --- /dev/null +++ b/Todo.Core/Models/TodoItem/TodoItemDTOCreate.cs @@ -0,0 +1,5 @@ +namespace Todo.Core.Models.TodoItem; + +public class TodoItemDTOCreate : TodoItemDtoManipulation +{ +} \ No newline at end of file diff --git a/Todo.Core/Models/TodoItem/TodoItemDTOManipulation.cs b/Todo.Core/Models/TodoItem/TodoItemDTOManipulation.cs new file mode 100644 index 00000000..1416dbff --- /dev/null +++ b/Todo.Core/Models/TodoItem/TodoItemDTOManipulation.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace Todo.Core.Models.TodoItem; + +public class TodoItemDtoManipulation +{ + [Required] + public string Name { get; init; } + + [Required] + public bool IsComplete { get; init; } +} \ No newline at end of file diff --git a/Todo.Core/Models/TodoItem/TodoItemDTOUpdate.cs b/Todo.Core/Models/TodoItem/TodoItemDTOUpdate.cs new file mode 100644 index 00000000..d6899c74 --- /dev/null +++ b/Todo.Core/Models/TodoItem/TodoItemDTOUpdate.cs @@ -0,0 +1,6 @@ +namespace Todo.Core.Models.TodoItem; + +public class TodoItemDTOUpdate : TodoItemDtoManipulation +{ + +} \ No newline at end of file diff --git a/Todo.Core/Todo.Core.csproj b/Todo.Core/Todo.Core.csproj index 583ad1f0..6b59291c 100644 --- a/Todo.Core/Todo.Core.csproj +++ b/Todo.Core/Todo.Core.csproj @@ -11,7 +11,7 @@ - + diff --git a/Todo.Infrastructure/DbContexts/TodoDbContext.cs b/Todo.Infrastructure/DbContexts/TodoDbContext.cs index bda7f878..a1e94a5e 100644 --- a/Todo.Infrastructure/DbContexts/TodoDbContext.cs +++ b/Todo.Infrastructure/DbContexts/TodoDbContext.cs @@ -5,7 +5,9 @@ namespace Todo.Infrastructure.DbContexts; public class TodoDbContext : DbContext { - public TodoDbContext(){} + public TodoDbContext() + { + } public TodoDbContext(DbContextOptions options) : base(options) { diff --git a/Todo.Infrastructure/Entities/TodoItem.cs b/Todo.Infrastructure/Entities/TodoItem.cs index d0d50a7a..38fe2a92 100644 --- a/Todo.Infrastructure/Entities/TodoItem.cs +++ b/Todo.Infrastructure/Entities/TodoItem.cs @@ -1,12 +1,14 @@ +using Microsoft.EntityFrameworkCore; + namespace Todo.Infrastructure.Entities; public class TodoItem { public long Id { get; set; } - public string Name { get; set; } + public string? Name { get; set; } public bool IsComplete { get; set; } - public string Secret { get; set; } + public string? Secret { get; set; } } \ No newline at end of file diff --git a/Todo.Infrastructure/Migrations/20230820174520_updateColumn.Designer.cs b/Todo.Infrastructure/Migrations/20230820174520_updateColumn.Designer.cs new file mode 100644 index 00000000..f5543d3a --- /dev/null +++ b/Todo.Infrastructure/Migrations/20230820174520_updateColumn.Designer.cs @@ -0,0 +1,51 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Todo.Infrastructure.DbContexts; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + [DbContext(typeof(TodoDbContext))] + [Migration("20230820174520_updateColumn")] + partial class updateColumn + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Todo.Infrastructure.Entities.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("IsComplete") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Secret") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TodoItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Todo.Infrastructure/Migrations/20230820174520_updateColumn.cs b/Todo.Infrastructure/Migrations/20230820174520_updateColumn.cs new file mode 100644 index 00000000..c4027bba --- /dev/null +++ b/Todo.Infrastructure/Migrations/20230820174520_updateColumn.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Todo.Infrastructure.Migrations +{ + /// + public partial class updateColumn : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Secret", + table: "TodoItems", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "TodoItems", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Secret", + table: "TodoItems", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Name", + table: "TodoItems", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + } + } +} diff --git a/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs b/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs index 31708c0d..3df0e315 100644 --- a/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs +++ b/Todo.Infrastructure/Migrations/TodoDbContextModelSnapshot.cs @@ -33,11 +33,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("Name") - .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("Secret") - .IsRequired() .HasColumnType("nvarchar(max)"); b.HasKey("Id"); diff --git a/Todo.Infrastructure/Repositories/Repository.cs b/Todo.Infrastructure/Repositories/Repository.cs index c21ce6fd..217b2cf8 100644 --- a/Todo.Infrastructure/Repositories/Repository.cs +++ b/Todo.Infrastructure/Repositories/Repository.cs @@ -22,10 +22,12 @@ public void Create(T entity) public async Task Read(Expression> condition) { - return await _context.Set() + T item = await _context.Set() .Where(condition) .AsNoTracking() .FirstOrDefaultAsync(); + + return item ?? Activator.CreateInstance(); // Return an empty instance if item is null } public void Update(T entity) @@ -45,8 +47,10 @@ public async Task SaveChanges() public async Task> GetAll() { - return await _context.Set() + List items = await _context.Set() .AsNoTracking() .ToListAsync(); + + return items; } } \ No newline at end of file From cb027a0d9659619bc469e6cbda96bb3395536382 Mon Sep 17 00:00:00 2001 From: kogo Date: Sun, 20 Aug 2023 22:30:09 +0400 Subject: [PATCH 8/9] add tests --- .DS_Store | Bin 6148 -> 6148 bytes Todo.API/Controllers/TodoItemsController.cs | 65 +++++++---- Todo.API/Startup.cs | 5 +- Todo.Test/Class1.cs | 5 - Todo.Test/Todo.Test.csproj | 12 ++ Todo.Test/TodoItemsTest.cs | 117 ++++++++++++++++++++ Todo.Test/Usings.cs | 1 + TodoApiDTO.sln | 10 +- 8 files changed, 183 insertions(+), 32 deletions(-) delete mode 100644 Todo.Test/Class1.cs create mode 100644 Todo.Test/TodoItemsTest.cs create mode 100644 Todo.Test/Usings.cs diff --git a/.DS_Store b/.DS_Store index 7e301461f810729529f59b068975ad43536ec92a..9a980d97a4ef58caeb09cc9fb47c738ad02156d2 100644 GIT binary patch delta 30 mcmZoMXfc@J&&WD4U^gS{WFE#Ho8K@>vrTN!*v!uHmmdI{LkbW8 delta 55 zcmZoMXfc@J&&W10U^gS%WFE#H(wqz-4EYQxK&%I3r7{#Vl%y0V=OpFl=WIU6sKK_G Io#QV*0G~|{l>h($ diff --git a/Todo.API/Controllers/TodoItemsController.cs b/Todo.API/Controllers/TodoItemsController.cs index fe591b7b..dd695641 100644 --- a/Todo.API/Controllers/TodoItemsController.cs +++ b/Todo.API/Controllers/TodoItemsController.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Mvc; using Todo.Core.Exceptions; using Todo.Core.Interfaces; -using Todo.Core.Models; using Todo.Core.Models.TodoItem; using Todo.Infrastructure.Entities; @@ -43,51 +42,79 @@ public async Task>> GetTodoItems() [HttpGet("{id}")] public async Task> GetTodoItem(long id) { - var item = await _itemService.Read(id); + try + { + var item = await _itemService.Read(id); - return Ok(_mapper.Map(item)); + return Ok(_mapper.Map(item)); + } + catch (Exception e) + { + Console.WriteLine(e); + return BadRequest(); + } } [HttpPut("{id}")] public async Task UpdateTodoItem(long id, [FromBody] TodoItemDTOUpdate todoItemDTO) { - var item = await TodoItemExists(id); - _mapper.Map(todoItemDTO, item); + try + { + var item = await TodoItemExists(id); + _mapper.Map(todoItemDTO, item); - await _itemService.Update(item); + await _itemService.Update(item); - return NoContent(); + return NoContent(); + } + catch (Exception e) + { + Console.WriteLine(e); + return BadRequest(); + } } - [HttpPost] public async Task> CreateTodoItem([FromBody] TodoItemDTOCreate todoItemDTO) { - var toEntity = _mapper.Map(todoItemDTO); - await _itemService.Create(toEntity); - - var createdItem = _mapper.Map(toEntity); + try + { + var toEntity = _mapper.Map(todoItemDTO); + await _itemService.Create(toEntity); - return CreatedAtRoute("TodoItemById", new { todoItemId = createdItem.Id }, createdItem); + var createdItem = _mapper.Map(toEntity); + return CreatedAtRoute("TodoItemById", new { todoItemId = createdItem.Id }, createdItem); + } + catch (Exception e) + { + Console.WriteLine(e); + return BadRequest(); + } } [HttpDelete("{id}")] public async Task DeleteTodoItem(long id) { - var item = await TodoItemExists(id); - await _itemService.Delete(item); + try + { + var item = await TodoItemExists(id); + + await _itemService.Delete(item); - return NoContent(); + return NoContent(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + return BadRequest(); + } } private async Task TodoItemExists(long id) { var item = await _itemService.Read(id); - if (item is null) - throw new Exception(); - return item; } } diff --git a/Todo.API/Startup.cs b/Todo.API/Startup.cs index ca47b76b..92433388 100644 --- a/Todo.API/Startup.cs +++ b/Todo.API/Startup.cs @@ -59,11 +59,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); - // You can define additional routes here endpoints.MapControllerRoute( name: "TodoItemById", - pattern: "api/todo/{todoItemId}", // Define your custom route pattern here - defaults: new { controller = "TodoItems", action = "GetTodoItem" }); // Controller and action names + pattern: "api/todo/{todoItemId}", + defaults: new { controller = "TodoItems", action = "GetTodoItem" }); }); app.UseSwagger(); diff --git a/Todo.Test/Class1.cs b/Todo.Test/Class1.cs deleted file mode 100644 index b8a0bb20..00000000 --- a/Todo.Test/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Todo.Test; - -public class Class1 -{ -} \ No newline at end of file diff --git a/Todo.Test/Todo.Test.csproj b/Todo.Test/Todo.Test.csproj index 28a66805..3b886f13 100644 --- a/Todo.Test/Todo.Test.csproj +++ b/Todo.Test/Todo.Test.csproj @@ -4,9 +4,21 @@ net7.0 enable enable + + false + + + + + + + + + + diff --git a/Todo.Test/TodoItemsTest.cs b/Todo.Test/TodoItemsTest.cs new file mode 100644 index 00000000..16044b13 --- /dev/null +++ b/Todo.Test/TodoItemsTest.cs @@ -0,0 +1,117 @@ +using AutoMapper; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Todo.API.Controllers; +using Todo.Core.Interfaces; +using Todo.Core.Models.TodoItem; +using Todo.Infrastructure.Entities; + +namespace Todo.Test; + +public class TodoItemsTest +{ + [TestFixture] + public class TodoItemsControllerTests + { + private readonly Mock mockMapper = new Mock(); + private readonly Mock mockTodoItemService = new Mock(); + + private readonly IReadOnlyList todoItems = + new List + { + new TodoItem { Id = 3, Name = "Buy groceries", IsComplete = false }, + new TodoItem { Id = 1, Name = "Do homework", IsComplete = true }, + new TodoItem { Id = 2, Name = "Read a book", IsComplete = false } + }; + + private TodoItemsController todoItemsController; + + [SetUp] + public void Setup() + { + // Configure AutoMapper mock + mockMapper.Setup(m => m.Map>(It.IsAny>())) + .Returns((List items) => items.Select(item => new TodoItemDTO())); + + mockMapper.Setup(m => m.Map(It.IsAny())) + .Returns((TodoItem item) => new TodoItemDTO()); + + // Configure TodoItemService mock + mockTodoItemService.Setup(service => service.GetAll()).ReturnsAsync(todoItems); + + mockTodoItemService.Setup(t => t.Read(1)).ReturnsAsync(todoItems[0]); + + todoItemsController = new TodoItemsController(mockTodoItemService.Object, mockMapper.Object); + } + + [Test] + public async Task GetTodoItems_ShouldReturnOkAndCallOnce_WhenTodoItemsExist() + { + // Act + var result = await todoItemsController.GetTodoItems(); + + // Assert + Assert.IsInstanceOf>>(result); + mockTodoItemService.Verify(mock => mock.GetAll(), Times.Once()); + } + + [Test] + public async Task ShouldReturnOkAndCallOnce_WhenTodoItemExists() + { + // Arrange + var todoItem = new TodoItem { Id = 13, Name = "Test Item", IsComplete = false }; + mockTodoItemService.Setup(t => t.Read(13)).ReturnsAsync(todoItem); + + // Act + var result = await todoItemsController.GetTodoItem(13); + + // Assert + Assert.IsInstanceOf(result.Result); + mockTodoItemService.Verify(mock => mock.Read(13), Times.Once()); + } + + [Test] + public async Task Create_Todo_Item_Should_Return_Created_At_Route_Result_With_Created_Todo_Item() + { + // Arrange + var mockCreationDto = new TodoItemDTOCreate() { Name = "Write unit tests", IsComplete = false }; + var mockEntity = new TodoItem { Id = 55, Name = "tests", IsComplete = false }; + + mockMapper.Setup(m => m.Map(mockCreationDto)).Returns(mockEntity); + mockTodoItemService.Setup(s => s.Create(mockEntity)).Returns(Task.CompletedTask); + + // Act + var result = await todoItemsController.CreateTodoItem(mockCreationDto); + + // Assert + Assert.IsInstanceOf(result.Result); // Use .Result here to access the ActionResult + } + + [Test] + public async Task UpdateTodoItem_ShouldReturnNoContentResult() + { + // Arrange + var updateItemDto = new TodoItemDTOUpdate() { Name = "Do workout", IsComplete = true }; + var updateEntity = new TodoItem { Id = 3, Name = "Do workout", IsComplete = true }; + + mockMapper.Setup(mapper => mapper.Map(updateItemDto, todoItems[0])).Returns(updateEntity); + mockTodoItemService.Setup(service => service.Update(updateEntity)).Returns(Task.CompletedTask); + + // Act + var result = await todoItemsController.UpdateTodoItem(3, updateItemDto); + + // Assert + Assert.IsInstanceOf(result); + } + + [Test] + public async Task DeleteTodoItem_ShouldReturnNoContentResult() + { + // Act + var result = await todoItemsController.DeleteTodoItem(3); + + // Assert + Assert.IsInstanceOf(result); + } + } +} \ No newline at end of file diff --git a/Todo.Test/Usings.cs b/Todo.Test/Usings.cs new file mode 100644 index 00000000..cefced49 --- /dev/null +++ b/Todo.Test/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/TodoApiDTO.sln b/TodoApiDTO.sln index bff3c84f..a7d4f6ec 100644 --- a/TodoApiDTO.sln +++ b/TodoApiDTO.sln @@ -9,7 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Core", "Todo.Core\Todo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Infrastructure", "Todo.Infrastructure\Todo.Infrastructure.csproj", "{9A288DEC-FCDC-4639-B301-F018AC43CE06}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Test", "Todo.Test\Todo.Test.csproj", "{B74218DA-1686-45DD-873A-A05FA5DF38A7}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Todo.Test", "Todo.Test\Todo.Test.csproj", "{E8932F55-4A85-45E9-9CA6-EB122D3BB153}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,10 +29,10 @@ Global {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Debug|Any CPU.Build.0 = Debug|Any CPU {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Release|Any CPU.ActiveCfg = Release|Any CPU {9A288DEC-FCDC-4639-B301-F018AC43CE06}.Release|Any CPU.Build.0 = Release|Any CPU - {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B74218DA-1686-45DD-873A-A05FA5DF38A7}.Release|Any CPU.Build.0 = Release|Any CPU + {E8932F55-4A85-45E9-9CA6-EB122D3BB153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8932F55-4A85-45E9-9CA6-EB122D3BB153}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8932F55-4A85-45E9-9CA6-EB122D3BB153}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8932F55-4A85-45E9-9CA6-EB122D3BB153}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From ad759fbaed58c4f8287f32492251b45273092d99 Mon Sep 17 00:00:00 2001 From: kogo Date: Sun, 20 Aug 2023 22:30:37 +0400 Subject: [PATCH 9/9] refactor tests --- Todo.Test/TodoItemsTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Todo.Test/TodoItemsTest.cs b/Todo.Test/TodoItemsTest.cs index 16044b13..8c80f52b 100644 --- a/Todo.Test/TodoItemsTest.cs +++ b/Todo.Test/TodoItemsTest.cs @@ -19,9 +19,9 @@ public class TodoItemsControllerTests private readonly IReadOnlyList todoItems = new List { - new TodoItem { Id = 3, Name = "Buy groceries", IsComplete = false }, - new TodoItem { Id = 1, Name = "Do homework", IsComplete = true }, - new TodoItem { Id = 2, Name = "Read a book", IsComplete = false } + new TodoItem { Id = 3, Name = "Iphone", IsComplete = false }, + new TodoItem { Id = 1, Name = "Samsung", IsComplete = true }, + new TodoItem { Id = 2, Name = "LG", IsComplete = false } }; private TodoItemsController todoItemsController;