├── CleanArchitectureWebAPI.Domian
├── CleanArchitectureWebAPI.Domian.csproj
├── Models
│ ├── Base
│ │ ├── BaseEntity.cs
│ │ ├── AuditableBaseEntity.cs
│ │ └── ProductEntity.cs
│ ├── Balm.cs
│ ├── Oil.cs
│ └── Soap.cs
└── Interfaces
│ ├── IOilRepository.cs
│ ├── ISoapRepository.cs
│ ├── IBalmRepository.cs
│ └── Base
│ └── IBaseRepository.cs
├── CleanArchitectureWebAPI.WebAPI
├── appsettings.Development.json
├── ViewModels
│ ├── SignInViewModel.cs
│ └── RegisterViewModel.cs
├── appsettings.json
├── Properties
│ └── launchSettings.json
├── Middlewares
│ └── ErrorLoggingMiddleware.cs
├── CleanArchitectureWebAPI.WebAPI.csproj
├── Program.cs
├── Startup.cs
├── Controllers
│ ├── OilsController.cs
│ ├── SoapsController.cs
│ ├── AccountController.cs
│ └── BalmsController.cs
└── Logs
│ ├── log20221129.txt
│ ├── log20221209.txt
│ ├── log20221207.txt
│ └── log20221202.txt
├── CleanArchitectureWebAPI.Application
├── ViewModels
│ ├── Balm
│ │ ├── BalmListViewModel.cs
│ │ └── BalmViewModel.cs
│ ├── Oil
│ │ ├── OilListViewModel.cs
│ │ └── OilViewModel.cs
│ ├── Soap
│ │ ├── SoapListViewModel.cs
│ │ └── SoapViewModel.cs
│ └── Base
│ │ └── BaseViewModel.cs
├── Interfaces
│ ├── IOilService.cs
│ ├── IBalmService.cs
│ └── ISoapService.cs
├── ServicesExtensions.cs
├── CleanArchitectureWebAPI.Application.csproj
├── Mappers
│ └── LibraryProfile.cs
└── Services
│ ├── OilService.cs
│ ├── SoapService.cs
│ └── BalmService.cs
├── CleanArchitectureWebAPI.Infrastructure.Data
├── Repositories
│ ├── OilRepository.cs
│ ├── SoapRepository.cs
│ ├── BalmRepository.cs
│ └── Base
│ │ └── BaseRepository.cs
├── CleanArchitectureWebAPI.Infrastructure.Data.csproj
├── ServiceExtensions.cs
├── Context
│ ├── LibraryDbContext.cs
│ └── LibraryDbContextSeed.cs
└── Migrations
│ ├── 20221129220853_init db.cs
│ ├── LibraryDbContextModelSnapshot.cs
│ └── 20221129220853_init db.Designer.cs
├── CleanArchitectureWebAPI.Infrastructure.IoC
├── CleanArchitectureWebAPI.Infrastructure.IoC.csproj
└── DependencyContainer.cs
├── CleanArchitectureWebAPI.Tests.XUnit
├── CleanArchitectureWebAPI.Tests.XUnit.csproj
├── BalmServiceTest.cs
├── SoapServiceTest.cs
└── OilServiceTest.cs
├── .gitattributes
├── CleanArchitectureWebAPI.sln
├── .gitignore
└── README.md
/CleanArchitectureWebAPI.Domian/CleanArchitectureWebAPI.Domian.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Models/Base/BaseEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Domian.Models.Base
6 | {
7 | public abstract class BaseEntity
8 | {
9 | public virtual Guid Id { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Models/Balm.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Models.Base;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Domian.Models
7 | {
8 | public class Balm : ProductEntity
9 | {
10 | public int Volume { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Balm/BalmListViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Application.ViewModels.Balm
6 | {
7 | public class BalmListViewModel
8 | {
9 | public IEnumerable Balms { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Oil/OilListViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Application.ViewModels.Oil
6 | {
7 | public class OilListViewModel
8 | {
9 | public IEnumerable Oils { get; set; }
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Soap/SoapListViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Application.ViewModels.Soap
6 | {
7 | public class SoapListViewModel
8 | {
9 | public IEnumerable Soaps { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Interfaces/IOilRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces.Base;
2 | using CleanArchitectureWebAPI.Domian.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace CleanArchitectureWebAPI.Domian.Interfaces
8 | {
9 | public interface IOilRepository : IBaseRepository
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Interfaces/ISoapRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces.Base;
2 | using CleanArchitectureWebAPI.Domian.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace CleanArchitectureWebAPI.Domian.Interfaces
8 | {
9 | public interface ISoapRepository : IBaseRepository
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Models/Oil.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Models.Base;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Domian.Models
7 | {
8 | public class Oil : ProductEntity
9 | {
10 | public string Scent { get; set; }
11 | public int LiquidVolume { get; set; }
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Interfaces/IBalmRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces.Base;
2 | using CleanArchitectureWebAPI.Domian.Models;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace CleanArchitectureWebAPI.Domian.Interfaces
8 | {
9 | public interface IBalmRepository : IBaseRepository
10 | {
11 | // This is where we put the methods specific for that class
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Soap/SoapViewModel.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.ViewModels.Base;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel.DataAnnotations;
5 | using System.Text;
6 |
7 | namespace CleanArchitectureWebAPI.Application.ViewModels.Soap
8 | {
9 | public class SoapViewModel : BaseViewModel
10 | {
11 | [MaxLength(50), Required]
12 | public string Edition { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Models/Soap.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Models.Base;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Domian.Models
7 | {
8 | public class Soap : ProductEntity
9 | {
10 | /*
11 | This is the place where we set the
12 | specific property for that class(product).
13 | */
14 | public string Edition { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/ViewModels/SignInViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace CleanArchitectureWebAPI.WebAPI.ViewModels
8 | {
9 | public class SignInViewModel
10 | {
11 | [Required]
12 | public string UserName { get; set; }
13 |
14 |
15 | [Required]
16 | public string Password { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Interfaces/IOilService.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.ViewModels.Oil;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Application.Interfaces
7 | {
8 | public interface IOilService
9 | {
10 | OilListViewModel GetOils();
11 | OilViewModel GetOilById(Guid id);
12 | OilViewModel AddOil(OilViewModel oilRequest);
13 | void EditOil(OilViewModel oilRequest);
14 | bool DeleteOil(Guid id);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Models/Base/AuditableBaseEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Domian.Models.Base
6 | {
7 | // Tracking data
8 | public abstract class AuditableBaseEntity : BaseEntity
9 | {
10 | public virtual string CreatedBy { get; set; }
11 | public virtual DateTime Created { get; set; }
12 | public virtual string LastModifiedBy { get; set; }
13 | public virtual DateTime LastModified { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Oil/OilViewModel.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.ViewModels.Base;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel.DataAnnotations;
5 | using System.Text;
6 |
7 | namespace CleanArchitectureWebAPI.Application.ViewModels.Oil
8 | {
9 | public class OilViewModel : BaseViewModel
10 | {
11 | [MaxLength(30), Required]
12 | public string Scent { get; set; }
13 |
14 | [Required]
15 | public int LiquidVolume { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Interfaces/IBalmService.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.ViewModels.Balm;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Application.Interfaces
7 | {
8 | public interface IBalmService
9 | {
10 | BalmListViewModel GetBalms();
11 | BalmViewModel GetBalmById(Guid id);
12 | BalmViewModel AddBalm(BalmViewModel balmRequest);
13 | void EditBalm(BalmViewModel balmRequest);
14 | bool DeleteBalm(Guid id);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Interfaces/ISoapService.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.ViewModels.Soap;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Application.Interfaces
7 | {
8 | public interface ISoapService
9 | {
10 | SoapListViewModel GetSoaps();
11 | SoapViewModel GetSoapById(Guid id);
12 | SoapViewModel AddSoap(SoapViewModel soapRequest);
13 | void EditSoap(SoapViewModel soapRequest);
14 | bool DeleteSoap(Guid id);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Interfaces/Base/IBaseRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Domian.Interfaces.Base
6 | {
7 | public interface IBaseRepository where T:class
8 | {
9 | /*
10 | This is where we put all the methods
11 | that are common for all entities.
12 | */
13 |
14 | IReadOnlyList GetAll();
15 | T GetById(Guid id);
16 | T Add(T entity);
17 | void Update(T entity);
18 | void Delete(T entity);
19 | void SaveChanges();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ServicesExtensions.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Reflection;
6 | using System.Text;
7 |
8 | namespace CleanArchitectureWebAPI.Application
9 | {
10 | public static class ServicesExtensions
11 | {
12 | public static void AddApplicationLayer(this IServiceCollection services)
13 | {
14 | // Setting AutoMapper
15 | services.AddAutoMapper(Assembly.GetExecutingAssembly());
16 |
17 | // we can add other services here
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Balm/BalmViewModel.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.ViewModels.Base;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel.DataAnnotations;
5 | using System.Text;
6 |
7 | namespace CleanArchitectureWebAPI.Application.ViewModels.Balm
8 | {
9 | public class BalmViewModel : BaseViewModel
10 | {
11 | /*
12 | This is the model that the user sees it in the presentation/view,
13 | so here you can use Data Annotation like [Required] or [MaxLength(20)]
14 | */
15 | [Required]
16 | public int Volume { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "Using": [],
4 | "MinimumLevel": {
5 | "Default": "Information",
6 | "Override": {
7 | "Microsoft": "Warning",
8 | "System": "Warning"
9 | }
10 | },
11 | "Enrich": [
12 | "FromLogContext",
13 | "WithMachineName",
14 | "WithProcessId",
15 | "WithThreadId"
16 | ],
17 | "Properties": {
18 | "ApplicationName": "webAPI"
19 | }
20 | },
21 | "AllowedHosts": "*",
22 | "ConnectionStrings": {
23 | "MyConnectionString": "Server=DESKTOP-C65AVR4;Database=BeardProductsDb;Trusted_Connection=True;"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Repositories/OilRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces;
2 | using CleanArchitectureWebAPI.Domian.Models;
3 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
4 | using CleanArchitectureWebAPI.Infrastructure.Data.Repositories.Base;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Text;
8 |
9 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Repositories
10 | {
11 | public class OilRepository : BaseRepository, IOilRepository
12 | {
13 | public OilRepository(LibraryDbContext dbContext) : base (dbContext)
14 | {
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Domian/Models/Base/ProductEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CleanArchitectureWebAPI.Domian.Models.Base
6 | {
7 | public class ProductEntity : AuditableBaseEntity
8 | {
9 | /*
10 | This is the class where we set all the
11 | properties that are common for all the products.
12 | */
13 | public string Brand { get; set; }
14 | public string Description { get; set; }
15 | public int UnitPrice { get; set; }
16 | public int UnitQuantity { get; set; }
17 | public string URL { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Repositories/SoapRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces;
2 | using CleanArchitectureWebAPI.Domian.Models;
3 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
4 | using CleanArchitectureWebAPI.Infrastructure.Data.Repositories.Base;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Text;
8 |
9 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Repositories
10 | {
11 | public class SoapRepository : BaseRepository, ISoapRepository
12 | {
13 | public SoapRepository(LibraryDbContext dbContext) : base(dbContext)
14 | {
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/CleanArchitectureWebAPI.Application.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/ViewModels/Base/BaseViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Text;
5 |
6 | namespace CleanArchitectureWebAPI.Application.ViewModels.Base
7 | {
8 | public class BaseViewModel
9 | {
10 | /*
11 | This is the base view model, so here you can use
12 | Data Annotations like [Required] or [MaxLength(20)]
13 | */
14 | public Guid Id { get; set; }
15 | public string Brand { get; set; }
16 | public string Description { get; set; }
17 | public int UnitPrice { get; set; }
18 | public int UnitQuantity { get; set; }
19 | public string URL { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/ViewModels/RegisterViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace CleanArchitectureWebAPI.WebAPI.ViewModels
8 | {
9 | public class RegisterViewModel
10 | {
11 | [Required, RegularExpression(@"^[a-zA-Z0-9_-]{3,15}$")]
12 | public string UserName { get; set; }
13 |
14 | [Required, EmailAddress]
15 | public string Email { get; set; }
16 |
17 | [Required]
18 | [RegularExpression(@"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,}$")]
19 | public string Password { get; set; }
20 |
21 | [Required, Compare("Password")]
22 | public string ConfirmPassword { get; set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Repositories/BalmRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces;
2 | using CleanArchitectureWebAPI.Domian.Models;
3 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
4 | using CleanArchitectureWebAPI.Infrastructure.Data.Repositories.Base;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Text;
8 |
9 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Repositories
10 | {
11 | public class BalmRepository : BaseRepository, IBalmRepository
12 | {
13 | public BalmRepository(LibraryDbContext dbContext) : base(dbContext)
14 | {
15 | /*
16 | This is the place where we create the logic for query,
17 | for saving and calling data for that entity.
18 | */
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.IoC/CleanArchitectureWebAPI.Infrastructure.IoC.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Mappers/LibraryProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.ViewModels.Balm;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Oil;
4 | using CleanArchitectureWebAPI.Application.ViewModels.Soap;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Text;
9 |
10 | namespace CleanArchitectureWebAPI.Application.Mappers
11 | {
12 | public class LibraryProfile : Profile
13 | {
14 | public LibraryProfile()
15 | {
16 | /*
17 | This is where we are mapping from
18 | entity to view model and vice versa.
19 | */
20 | CreateMap().ReverseMap();
21 | CreateMap().ReverseMap();
22 | CreateMap().ReverseMap();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:59046",
8 | "sslPort": 44342
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "CleanArchitectureWebAPI.WebAPI": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "api/soaps",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Tests.XUnit/CleanArchitectureWebAPI.Tests.XUnit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 | all
19 |
20 |
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 | all
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.IoC/DependencyContainer.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.Interfaces;
2 | using CleanArchitectureWebAPI.Application.Services;
3 | using CleanArchitectureWebAPI.Domian.Interfaces;
4 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
5 | using CleanArchitectureWebAPI.Infrastructure.Data.Repositories;
6 | using Microsoft.AspNetCore.Authentication.JwtBearer;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.EntityFrameworkCore;
9 | using Microsoft.Extensions.Configuration;
10 | using Microsoft.Extensions.DependencyInjection;
11 | using Microsoft.IdentityModel.Tokens;
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Text;
15 |
16 | namespace CleanArchitectureWebAPI.Infrastructure.IoC
17 | {
18 | public static class DependencyContainer
19 | {
20 | public static void AddIoCService(this IServiceCollection services)
21 | {
22 | // IoC - Inversion Of Control
23 | // Application
24 | services.AddScoped();
25 | services.AddScoped();
26 | services.AddScoped();
27 |
28 | // Domain.Interfaces > Infrastructure.Data.Repositories
29 | services.AddScoped();
30 | services.AddScoped();
31 | services.AddScoped();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/CleanArchitectureWebAPI.Infrastructure.Data.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Repositories/Base/BaseRepository.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Interfaces.Base;
2 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
3 | using Microsoft.EntityFrameworkCore;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 |
9 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Repositories.Base
10 | {
11 | public class BaseRepository : IBaseRepository where T : class
12 | {
13 | /*
14 | This is where we communicate with database,
15 | and it stands for all the classes.
16 | */
17 | private readonly LibraryDbContext _dbContext;
18 | public BaseRepository(LibraryDbContext dbContext)
19 | {
20 | _dbContext = dbContext;
21 | }
22 | public T Add(T entity)
23 | {
24 | _dbContext.Set().Add(entity);
25 | return entity;
26 | }
27 |
28 | public void Delete(T entity)
29 | {
30 | _dbContext.Set().Remove(entity);
31 | }
32 |
33 | public IReadOnlyList GetAll()
34 | {
35 | return _dbContext.Set().ToList();
36 | }
37 |
38 | public T GetById(Guid id)
39 | {
40 | return _dbContext.Set().Find(id);
41 | }
42 |
43 | public void Update(T entity)
44 | {
45 | _dbContext.Entry(entity).State = EntityState.Modified;
46 | }
47 |
48 | public void SaveChanges()
49 | {
50 | _dbContext.SaveChanges();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Middlewares/ErrorLoggingMiddleware.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Serilog;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace CleanArchitectureWebAPI.WebAPI.Middlewares
9 | {
10 | public class ErrorLoggingMiddleware
11 | {
12 | const string MessageTemplate = "HTTP {RequestMethod} {RequestPath} responded {RequestCode}";
13 | static readonly ILogger Log = Serilog.Log.ForContext();
14 | private readonly RequestDelegate _next;
15 |
16 | public ErrorLoggingMiddleware(RequestDelegate next)
17 | {
18 | _next = next;
19 | }
20 |
21 | public async Task Invoke(HttpContext httpContext)
22 | {
23 | try
24 | {
25 | await _next(httpContext);
26 | }
27 | catch (Exception e)
28 | {
29 | var request = httpContext.Request;
30 |
31 | var log = Log
32 | .ForContext("RequestHeaders", request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), destructureObjects: true)
33 | .ForContext("RequestHost", request.Host)
34 | .ForContext("RequestProtocol", request.Protocol);
35 |
36 | if (request.HasFormContentType)
37 | log = log.ForContext("RequestForm", request.Form.ToDictionary(v => v.Key, v => v.Value.ToString()));
38 |
39 | log.Error(e, MessageTemplate, httpContext.Request.Method, httpContext.Request.Path, 500);
40 |
41 | throw;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/ServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
2 | using Microsoft.AspNetCore.Authentication.JwtBearer;
3 | using Microsoft.AspNetCore.Identity;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.IdentityModel.Tokens;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Text;
10 |
11 | namespace CleanArchitectureWebAPI.Infrastructure.Data
12 | {
13 | public static class ServiceExtensions
14 | {
15 | public static void AddIdentityInfrastructure(this IServiceCollection services)
16 | {
17 | // Setting Identity
18 | services.AddIdentity()
19 | .AddEntityFrameworkStores()
20 | .AddDefaultTokenProviders();
21 |
22 | // Setting JWT Token
23 | var singingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("this-is-my-secret-key"));
24 | var tokenValidationParameters = new TokenValidationParameters()
25 | {
26 | IssuerSigningKey = singingKey,
27 | ValidateIssuer = false,
28 | ValidateAudience = false,
29 | ValidateLifetime = true,
30 | ClockSkew = TimeSpan.Zero
31 | };
32 | services.AddAuthentication(x => x.DefaultAuthenticateScheme = JwtBearerDefaults
33 | .AuthenticationScheme)
34 | .AddJwtBearer(jwt =>
35 | {
36 | jwt.TokenValidationParameters = tokenValidationParameters;
37 | });
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/CleanArchitectureWebAPI.WebAPI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 |
18 | all
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Services/OilService.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.Interfaces;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Oil;
4 | using CleanArchitectureWebAPI.Domian.Interfaces;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Text;
9 |
10 | namespace CleanArchitectureWebAPI.Application.Services
11 | {
12 | public class OilService : IOilService
13 | {
14 | private readonly IOilRepository _oilRepository;
15 | private readonly IMapper _mapper;
16 | public OilService(IOilRepository oilRepository, IMapper mapper)
17 | {
18 | _mapper = mapper;
19 | _oilRepository = oilRepository;
20 | }
21 | public OilViewModel AddOil(OilViewModel oilRequest)
22 | {
23 | var oil = _mapper.Map(oilRequest);
24 | oil.Id = Guid.NewGuid();
25 |
26 | var addedOil = _oilRepository.Add(oil);
27 |
28 | _oilRepository.SaveChanges();
29 |
30 | return _mapper.Map(addedOil);
31 | }
32 |
33 | public bool DeleteOil(Guid id)
34 | {
35 | var oil = _oilRepository.GetById(id);
36 |
37 | if (oil != null)
38 | {
39 | _oilRepository.Delete(oil);
40 | _oilRepository.SaveChanges();
41 |
42 | return true;
43 | }
44 | else
45 | {
46 | return false;
47 | }
48 | }
49 |
50 | public void EditOil(OilViewModel oilRequest)
51 | {
52 | var oil = _mapper.Map(oilRequest);
53 | _oilRepository.Update(oil);
54 |
55 | _oilRepository.SaveChanges();
56 | }
57 |
58 | public OilViewModel GetOilById(Guid id)
59 | {
60 | var oil = _oilRepository.GetById(id);
61 | return _mapper.Map(oil);
62 | }
63 |
64 | public OilListViewModel GetOils()
65 | {
66 | var oils = _oilRepository.GetAll();
67 | var oilListViewModel = _mapper.Map>(oils);
68 |
69 | return new OilListViewModel()
70 | {
71 | Oils = oilListViewModel
72 | };
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Services/SoapService.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.Interfaces;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Soap;
4 | using CleanArchitectureWebAPI.Domian.Interfaces;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Text;
9 |
10 | namespace CleanArchitectureWebAPI.Application.Services
11 | {
12 | public class SoapService : ISoapService
13 | {
14 | private readonly ISoapRepository _soapRepository;
15 | private readonly IMapper _mapper;
16 | public SoapService(ISoapRepository soapRepository, IMapper mapper)
17 | {
18 | _soapRepository = soapRepository;
19 | _mapper = mapper;
20 | }
21 | public SoapViewModel AddSoap(SoapViewModel soapRequest)
22 | {
23 | var soap = _mapper.Map(soapRequest);
24 | soap.Id = Guid.NewGuid();
25 |
26 | var addedSoap = _soapRepository.Add(soap);
27 |
28 | _soapRepository.SaveChanges();
29 |
30 | return _mapper.Map(addedSoap);
31 | }
32 |
33 | public bool DeleteSoap(Guid id)
34 | {
35 | var soap = _soapRepository.GetById(id);
36 |
37 | if (soap != null)
38 | {
39 | _soapRepository.Delete(soap);
40 | _soapRepository.SaveChanges();
41 |
42 | return true;
43 | }
44 | else
45 | {
46 | return false;
47 | }
48 | }
49 |
50 | public void EditSoap(SoapViewModel soapRequest)
51 | {
52 | var soap = _mapper.Map(soapRequest);
53 | _soapRepository.Update(soap);
54 |
55 | _soapRepository.SaveChanges();
56 | }
57 |
58 | public SoapViewModel GetSoapById(Guid id)
59 | {
60 | var soap = _soapRepository.GetById(id);
61 | return _mapper.Map(soap);
62 | }
63 |
64 | public SoapListViewModel GetSoaps()
65 | {
66 | var soaps = _soapRepository.GetAll();
67 | var soapListViewModel = _mapper.Map>(soaps);
68 |
69 | return new SoapListViewModel()
70 | {
71 | Soaps = soapListViewModel
72 | };
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Application/Services/BalmService.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.Interfaces;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Balm;
4 | using CleanArchitectureWebAPI.Domian.Interfaces;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Text;
9 |
10 | namespace CleanArchitectureWebAPI.Application.Services
11 | {
12 | public class BalmService : IBalmService
13 | {
14 | /*
15 | This is where all the logic is, in the Services.
16 | */
17 | private readonly IBalmRepository _balmRepository;
18 | private readonly IMapper _mapper;
19 | public BalmService(IBalmRepository balmRepository, IMapper mapper)
20 | {
21 | _mapper = mapper;
22 | _balmRepository = balmRepository;
23 | }
24 | public BalmViewModel AddBalm(BalmViewModel balmRequest)
25 | {
26 | var balm = _mapper.Map(balmRequest);
27 | balm.Id = Guid.NewGuid();
28 |
29 | var addedBalm = _balmRepository.Add(balm);
30 |
31 | _balmRepository.SaveChanges();
32 |
33 | return _mapper.Map(addedBalm);
34 | }
35 |
36 | public bool DeleteBalm(Guid id)
37 | {
38 | var balm = _balmRepository.GetById(id);
39 |
40 | if (balm != null)
41 | {
42 | _balmRepository.Delete(balm);
43 | _balmRepository.SaveChanges();
44 |
45 | return true;
46 | }
47 | else
48 | {
49 | return false;
50 | }
51 | }
52 |
53 | public void EditBalm(BalmViewModel balmRequest)
54 | {
55 | var balm = _mapper.Map(balmRequest);
56 | _balmRepository.Update(balm);
57 |
58 | _balmRepository.SaveChanges();
59 | }
60 |
61 | public BalmViewModel GetBalmById(Guid id)
62 | {
63 | var balm = _balmRepository.GetById(id);
64 | return _mapper.Map(balm);
65 | }
66 |
67 | public BalmListViewModel GetBalms()
68 | {
69 | var balms = _balmRepository.GetAll();
70 | var balmListViewModel = _mapper.Map>(balms);
71 |
72 | return new BalmListViewModel()
73 | {
74 | Balms = balmListViewModel
75 | };
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Program.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using Microsoft.Extensions.Logging;
7 | using Serilog;
8 | using Serilog.Formatting.Json;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Threading.Tasks;
13 |
14 | namespace CleanArchitectureWebAPI.WebAPI
15 | {
16 | public class Program
17 | {
18 | public static void Main(string[] args)
19 | {
20 | // Read configuration from appsetting.json
21 | var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
22 |
23 | // Initialize logger
24 | Log.Logger = new LoggerConfiguration()
25 | .ReadFrom.Configuration(config)
26 | .WriteTo.File("Logs/log.txt", rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp} {Message}{NewLine:1}{Exception:1}")
27 | .WriteTo.File(new JsonFormatter(), "Logs/log.json", rollingInterval: RollingInterval.Day)
28 | .WriteTo.Console()
29 | .CreateLogger();
30 |
31 | try
32 | {
33 | Log.Information("Application is starting");
34 |
35 | var host = CreateHostBuilder(args).Build();
36 |
37 | SeedDatabase(host);
38 |
39 | host.Run();
40 | }
41 | catch (Exception e)
42 | {
43 | Log.Error(e, "The application failed to start!");
44 | }
45 | finally
46 | {
47 | Log.CloseAndFlush();
48 | }
49 | }
50 |
51 | public static IHostBuilder CreateHostBuilder(string[] args) =>
52 | Host.CreateDefaultBuilder(args)
53 | .UseSerilog()
54 | .ConfigureWebHostDefaults(webBuilder =>
55 | {
56 | webBuilder.UseStartup();
57 | });
58 |
59 | private static void SeedDatabase(IHost host)
60 | {
61 | using (var scope = host.Services.CreateScope())
62 | {
63 | var services = scope.ServiceProvider;
64 | var loggerFactory = services.GetRequiredService();
65 |
66 | try
67 | {
68 | var libraryDbContext = services.GetRequiredService();
69 | LibraryDbContextSeed.SeedAsync(libraryDbContext, loggerFactory).Wait();
70 | }
71 | catch (Exception exception)
72 | {
73 | var logger = loggerFactory.CreateLogger();
74 | logger.LogError(exception, "An error occurred seeding the DB.");
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Context/LibraryDbContext.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Models;
2 | using CleanArchitectureWebAPI.Domian.Models.Base;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text;
10 |
11 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Context
12 | {
13 | public class LibraryDbContext : IdentityDbContext
14 | {
15 | private IHttpContextAccessor _httpContextAccessor;
16 | public LibraryDbContext()
17 | {
18 | }
19 |
20 | public LibraryDbContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options)
21 | {
22 | _httpContextAccessor = httpContextAccessor;
23 | }
24 |
25 | public DbSet Soaps { get; set; }
26 | public DbSet Balms { get; set; }
27 | public DbSet Oils { get; set; }
28 |
29 | public override int SaveChanges()
30 | {
31 | // Get all the entities that inherit from AuditableEntity
32 | // and have a state of Added or Modified
33 | var entries = ChangeTracker
34 | .Entries()
35 | .Where(e => e.Entity is AuditableBaseEntity && (
36 | e.State == EntityState.Added
37 | || e.State == EntityState.Modified));
38 |
39 | // For each entity we will set the Audit properties
40 | foreach (var entityEntry in entries)
41 | {
42 | // If the entity state is Added let's set
43 | // the CreatedAt and CreatedBy properties
44 | if (entityEntry.State == EntityState.Added)
45 | {
46 | ((AuditableBaseEntity)entityEntry.Entity).Created = DateTime.UtcNow;
47 | ((AuditableBaseEntity)entityEntry.Entity).CreatedBy = this._httpContextAccessor?.HttpContext?.User?.Identity?.Name ?? "MyApp";
48 | }
49 | else
50 | {
51 | // If the state is Modified then we don't want
52 | // to modify the CreatedAt and CreatedBy properties
53 | // so we set their state as IsModified to false
54 | Entry((AuditableBaseEntity)entityEntry.Entity).Property(p => p.Created).IsModified = false;
55 | Entry((AuditableBaseEntity)entityEntry.Entity).Property(p => p.CreatedBy).IsModified = false;
56 | }
57 |
58 | // In any case we always want to set the properties
59 | // ModifiedAt and ModifiedBy
60 | ((AuditableBaseEntity)entityEntry.Entity).LastModified = DateTime.UtcNow;
61 | ((AuditableBaseEntity)entityEntry.Entity).LastModifiedBy = this._httpContextAccessor?.HttpContext?.User?.Identity?.Name ?? "MyApp";
62 | }
63 | // After we set all the needed properties
64 | // we call the base implementation of SaveChanges
65 | // to actually save our entities in the database
66 | return base.SaveChanges();
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Startup.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application;
2 | using CleanArchitectureWebAPI.Infrastructure.Data;
3 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
4 | using CleanArchitectureWebAPI.Infrastructure.IoC;
5 | using CleanArchitectureWebAPI.WebAPI.Middlewares;
6 | using Microsoft.AspNetCore.Authentication.JwtBearer;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.AspNetCore.Hosting;
9 | using Microsoft.AspNetCore.HttpsPolicy;
10 | using Microsoft.AspNetCore.Identity;
11 | using Microsoft.AspNetCore.Mvc;
12 | using Microsoft.EntityFrameworkCore;
13 | using Microsoft.Extensions.Configuration;
14 | using Microsoft.Extensions.DependencyInjection;
15 | using Microsoft.Extensions.Hosting;
16 | using Microsoft.Extensions.Logging;
17 | using Microsoft.IdentityModel.Tokens;
18 | using Serilog;
19 | using System;
20 | using System.Collections.Generic;
21 | using System.Linq;
22 | using System.Text;
23 | using System.Threading.Tasks;
24 |
25 | namespace CleanArchitectureWebAPI.WebAPI
26 | {
27 | public class Startup
28 | {
29 | public Startup(IConfiguration configuration)
30 | {
31 | Configuration = configuration;
32 | }
33 |
34 | public IConfiguration Configuration { get; }
35 |
36 | // This method gets called by the runtime. Use this method to add services to the container.
37 | public void ConfigureServices(IServiceCollection services)
38 | {
39 | // Setting Response Caching
40 | services.AddResponseCaching();
41 |
42 | // Setting In Memory Cache
43 | services.AddMemoryCache();
44 |
45 | services.AddControllers();
46 |
47 | services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
48 | {
49 | builder.AllowAnyOrigin()
50 | .AllowAnyMethod()
51 | .AllowAnyHeader();
52 | }));
53 |
54 | // This is where we register the context
55 | services.AddDbContext(options =>
56 | options.UseSqlServer(Configuration.GetConnectionString("MyConnectionString")));
57 |
58 | // Registering the Identity Infrastructure
59 | services.AddIdentityInfrastructure();
60 |
61 | // Registering Inversion Of Control
62 | services.AddIoCService();
63 |
64 | // Registering the Swagger
65 | services.AddSwaggerDocument();
66 |
67 | // Registering the HttpContext Accessor which we use for auditing in the LibraryDbContext
68 | services.AddHttpContextAccessor();
69 |
70 | // Registering the AutoMapper that mapps from entity to view model and vice versa
71 | services.AddApplicationLayer();
72 | }
73 |
74 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
75 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
76 | {
77 | if (env.IsDevelopment())
78 | {
79 | app.UseDeveloperExceptionPage();
80 | }
81 |
82 | app.UseHttpsRedirection();
83 |
84 | // Using the Response Caching
85 | app.UseResponseCaching();
86 |
87 | // Use Serilog Logger
88 | app.UseSerilogRequestLogging();
89 |
90 | // Error Logging middleware
91 | app.UseMiddleware();
92 |
93 | app.UseRouting();
94 |
95 | app.UseCors("MyPolicy");
96 |
97 | app.UseAuthorization();
98 |
99 | app.UseAuthentication();
100 |
101 | app.UseEndpoints(endpoints =>
102 | {
103 | endpoints.MapControllers();
104 | });
105 |
106 | app.UseOpenApi();
107 |
108 | app.UseSwaggerUi3();
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Tests.XUnit/BalmServiceTest.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.Services;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Balm;
4 | using CleanArchitectureWebAPI.Domian.Interfaces;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using FluentAssertions;
7 | using Moq;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Text;
11 | using Xunit;
12 |
13 | namespace CleanArchitectureWebAPI.Tests.XUnit
14 | {
15 | public class BalmServiceTest
16 | {
17 | [Fact]
18 | public void GetBalms_NotExistingBalms_ResultShouldBeEmpty()
19 | {
20 | // arrange
21 | var mockBalmRepository = new Mock();
22 | var mockMapper = new Mock();
23 |
24 | // act
25 | var balmService = new BalmService(mockBalmRepository.Object, mockMapper.Object);
26 | var result = balmService.GetBalms();
27 |
28 | // assert
29 | result.Balms.Should().BeEmpty();
30 | }
31 |
32 | [Fact]
33 | public void GetBalms_NotExistingBalms_ResultShouldBeOfTypeBalmListViewModel()
34 | {
35 | // arrance
36 | var mockBalmRepository = new Mock();
37 | var mockMapper = new Mock();
38 |
39 | // act
40 | var balmService = new BalmService(mockBalmRepository.Object, mockMapper.Object);
41 | var result = balmService.GetBalms();
42 |
43 | // assert
44 | result.Should().BeOfType(typeof(BalmListViewModel));
45 | }
46 |
47 | [Fact]
48 | public void GetBalms_ExistingBalms_ResultShouldBeTwoBalms()
49 | {
50 | // arrange
51 | var mockBalmRepository = new Mock();
52 |
53 | var mockBalms = new List
54 | {
55 | new Balm
56 | {
57 | Id = Guid.NewGuid(),
58 | Volume = 30,
59 | Brand = "Balm Brand 1",
60 | Description = "Balm Description 1",
61 | UnitQuantity = 100,
62 | UnitPrice = 30,
63 | URL = "img/balm1.jpg"
64 | },
65 | new Balm
66 | {
67 | Id = Guid.NewGuid(),
68 | Volume = 33,
69 | Brand = "Balm Brand 2",
70 | Description = "Balm Description 2",
71 | UnitQuantity = 100,
72 | UnitPrice = 32,
73 | URL = "img/balm2.jpg"
74 | }
75 | };
76 |
77 | mockBalmRepository.Setup(x => x.GetAll()).Returns(mockBalms);
78 |
79 | var config = new MapperConfiguration(cfg =>
80 | {
81 | cfg.CreateMap();
82 | });
83 | var mapper = config.CreateMapper();
84 |
85 | // act
86 | var balmService = new BalmService(mockBalmRepository.Object, mapper);
87 | var result = balmService.GetBalms();
88 |
89 | // assert
90 | result.Balms.Should().HaveCount(2);
91 | }
92 |
93 | [Fact]
94 | public void GetBalmById_NotExpectingBalm_ResultShouldBeNull()
95 | {
96 | // arrance
97 | var mockBalmRepository = new Mock();
98 | var mockMapper = new Mock();
99 |
100 | // act
101 | var balmService = new BalmService(mockBalmRepository.Object, mockMapper.Object);
102 | var result = balmService.GetBalmById(Guid.NewGuid());
103 |
104 | // assert
105 | result.Should().BeNull();
106 | }
107 |
108 | [Fact]
109 | public void GetBalmById_ExpectingBalm_ResultShouldReturnBalmViewModel()
110 | {
111 | // arrance
112 | var mockBalmRepository = new Mock();
113 |
114 | var balm = new Balm()
115 | {
116 | Id = Guid.NewGuid(),
117 | Volume = 30,
118 | Brand = "Balm Brand 1",
119 | Description = "Balm Description 1",
120 | UnitQuantity = 100,
121 | UnitPrice = 30,
122 | URL = "img/balm1.jpg"
123 | };
124 |
125 | mockBalmRepository.Setup(x => x.GetById(It.IsAny())).Returns(balm);
126 |
127 | var config = new MapperConfiguration(cfg =>
128 | {
129 | cfg.CreateMap();
130 | });
131 | var mapper = config.CreateMapper();
132 |
133 | // act
134 | var balmService = new BalmService(mockBalmRepository.Object, mapper);
135 | var result = balmService.GetBalmById(Guid.NewGuid());
136 |
137 | // assert
138 | result.Should().BeOfType(typeof(BalmViewModel));
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Tests.XUnit/SoapServiceTest.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.Services;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Soap;
4 | using CleanArchitectureWebAPI.Domian.Interfaces;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using FluentAssertions;
7 | using Moq;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Text;
11 | using Xunit;
12 |
13 | namespace CleanArchitectureWebAPI.Tests.XUnit
14 | {
15 | public class SoapServiceTest
16 | {
17 | [Fact]
18 | public void GetSoaps_NotExistingSoaps_ResultShouldBeEmpty()
19 | {
20 | // arrange
21 | var mockSoapRepository = new Mock();
22 | var mockMapper = new Mock();
23 |
24 | // act
25 | var soapService = new SoapService(mockSoapRepository.Object, mockMapper.Object);
26 | var result = soapService.GetSoaps();
27 |
28 | // assert
29 | result.Soaps.Should().BeEmpty();
30 | }
31 |
32 | [Fact]
33 | public void GetSoaps_NotExistingSoaps_ResultShouldBeOfTypeSoapListViewModel()
34 | {
35 | // arrange
36 | var mockSoapRepository = new Mock();
37 | var mockMapper = new Mock();
38 |
39 | // act
40 | var soapService = new SoapService(mockSoapRepository.Object, mockMapper.Object);
41 | var result = soapService.GetSoaps();
42 |
43 | // arrange
44 | result.Should().BeOfType(typeof(SoapListViewModel));
45 | }
46 |
47 | [Fact]
48 | public void GetSoaps_ExistingSoaps_ResultShouldBeTwoSoaps()
49 | {
50 | // arrance
51 | var mockSoapRepository = new Mock();
52 | var mockedSoaps = new List
53 | {
54 | new Soap
55 | {
56 | Id = Guid.NewGuid(),
57 | Brand = "Soap Brand 1",
58 | Edition = "Soap Edition 1",
59 | Description = "Soap Description 1",
60 | UnitPrice = 20,
61 | UnitQuantity = 100,
62 | URL = "img/soap1.jpeg"
63 | },
64 | new Soap
65 | {
66 | Id = Guid.NewGuid(),
67 | Brand = "Soap Brand 2",
68 | Edition = "Soap Edition 2",
69 | Description = "Soap Description 2",
70 | UnitPrice = 21,
71 | UnitQuantity = 100,
72 | URL = "img/soap2.jpeg"
73 | }
74 | };
75 |
76 | mockSoapRepository.Setup(x => x.GetAll()).Returns(mockedSoaps);
77 |
78 | var config = new MapperConfiguration(cfg =>
79 | {
80 | cfg.CreateMap();
81 | });
82 | var mapper = config.CreateMapper();
83 |
84 | // act
85 | var soapService = new SoapService(mockSoapRepository.Object, mapper);
86 | var result = soapService.GetSoaps();
87 |
88 | // assert
89 | result.Soaps.Should().HaveCount(2);
90 | }
91 |
92 |
93 | [Fact]
94 | public void GetSoapById_NotExpectingSoap_ResultShouldBeNull()
95 | {
96 | // arrange
97 | var mockSoapRepository = new Mock();
98 | var mockMapper = new Mock();
99 |
100 | // act
101 | var soapService = new SoapService(mockSoapRepository.Object, mockMapper.Object);
102 | var result = soapService.GetSoapById(Guid.NewGuid());
103 |
104 | // assert
105 | result.Should().BeNull();
106 | }
107 |
108 | [Fact]
109 | public void GetSoapById_ExpectingSoap_ResultShouldReturnSoapViewModel()
110 | {
111 | // arrange
112 | var mockSoapRepository = new Mock();
113 |
114 | var mockSoap = new Soap()
115 | {
116 | Id = Guid.NewGuid(),
117 | Brand = "Soap Brand 1",
118 | Edition = "Soap Edition 1",
119 | Description = "Soap Description 1",
120 | UnitPrice = 20,
121 | UnitQuantity = 100,
122 | URL = "img/soap1.jpeg"
123 | };
124 |
125 | mockSoapRepository.Setup(x => x.GetById(It.IsAny())).Returns(mockSoap);
126 |
127 | var config = new MapperConfiguration(cfg =>
128 | {
129 | cfg.CreateMap();
130 | });
131 |
132 | var mapper = config.CreateMapper();
133 |
134 | // act
135 | var soapService = new SoapService(mockSoapRepository.Object, mapper);
136 | var result = soapService.GetSoapById(Guid.NewGuid());
137 |
138 | // assert
139 | result.Should().BeOfType();
140 | }
141 |
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Controllers/OilsController.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.Interfaces;
2 | using CleanArchitectureWebAPI.Application.ViewModels.Oil;
3 | using Microsoft.AspNetCore.Authentication.JwtBearer;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.Extensions.Caching.Memory;
8 | using NSwag.Annotations;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Net;
13 | using System.Threading.Tasks;
14 |
15 | namespace CleanArchitectureWebAPI.WebAPI.Controllers
16 | {
17 | [Route("api/[controller]")]
18 | [OpenApiTag("Oils", Description = "Methods to work with Oils")]
19 | [ApiController]
20 | public class OilsController : Controller
21 | {
22 | private readonly IOilService _oilService;
23 | private IMemoryCache _memoryCache;
24 | private string _allOilsKey = "All_Oils_Cache";
25 | public OilsController(IOilService oilService, IMemoryCache memoryCache)
26 | {
27 | _oilService = oilService;
28 | _memoryCache = memoryCache;
29 | }
30 |
31 |
32 |
33 | [HttpGet] //URL/api/oils http metod Get
34 | [SwaggerResponse(HttpStatusCode.OK, typeof(OilListViewModel), Description = "Successfully Returned List Of Oils")]
35 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "List Of Oils Is Empty")]
36 | public IActionResult GetAll()
37 | {
38 | OilListViewModel oilListViewModel;
39 | oilListViewModel = (OilListViewModel)_memoryCache.Get(_allOilsKey);
40 |
41 | if (oilListViewModel == null)
42 | {
43 | oilListViewModel = _oilService.GetOils();
44 |
45 | _memoryCache.Set(_allOilsKey, oilListViewModel, new MemoryCacheEntryOptions()
46 | .SetSlidingExpiration(TimeSpan.FromMinutes(15))
47 | .SetAbsoluteExpiration(TimeSpan.FromHours(1)));
48 | }
49 | if (oilListViewModel != null)
50 | {
51 | return Ok(oilListViewModel);
52 | }
53 | else
54 | {
55 | return NotFound();
56 | }
57 | }
58 |
59 |
60 | [HttpGet("{id}")] //URL/api/oils/id http metod Get
61 | [SwaggerResponse(HttpStatusCode.OK, typeof(OilViewModel), Description = "Successfully Returned Oil Model")]
62 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "There Is No Oil Model With That Id")]
63 | public IActionResult GetById(Guid id)
64 | {
65 | var oil = _oilService.GetOilById(id);
66 | if (oil != null)
67 | {
68 | return Ok(oil);
69 | }
70 | else
71 | {
72 | return NotFound(id);
73 | }
74 | }
75 |
76 |
77 | [HttpPost] //URL/api/oils http metod Post
78 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
79 | [SwaggerResponse(HttpStatusCode.Unauthorized, null, Description = "You Don't Have Authorization For This Request")]
80 | [SwaggerResponse(HttpStatusCode.OK, typeof(OilViewModel), Description = "Ok")]
81 | [SwaggerResponse(HttpStatusCode.Created, typeof(OilViewModel), Description = "Oil Model Created")]
82 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "Oil Model Not Found")]
83 | public IActionResult AddOrEdit(OilViewModel model)
84 | {
85 | if (model.Id == Guid.Empty)
86 | {
87 | model = _oilService.AddOil(model);
88 | }
89 | else
90 | {
91 | _oilService.EditOil(model);
92 | }
93 | _memoryCache.Remove(_allOilsKey);
94 | return Ok(model);
95 | }
96 |
97 |
98 | [HttpDelete("{id}")] //URL/api/oils/id http metod Delete
99 | [ResponseCache(Duration = 300, VaryByQueryKeys = new string[] { "id" })]
100 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
101 | [SwaggerResponse(HttpStatusCode.OK, typeof(OilViewModel), Description = "Successfully Deleted Oil Model")]
102 | [SwaggerResponse(HttpStatusCode.NotFound, typeof(Guid), Description = "Oil Model You Want To Delete Doesn't Exist")]
103 | [SwaggerResponse(HttpStatusCode.Unauthorized, null, Description = "You Don't Have Authorization For This Request")]
104 | public IActionResult Delete(Guid id)
105 | {
106 | var oil = _oilService.GetOilById(id);
107 | var status = false;
108 |
109 | if (oil != null)
110 | {
111 | status = _oilService.DeleteOil(id);
112 | _memoryCache.Remove(_allOilsKey);
113 | return Ok(status);
114 | }
115 | else
116 | {
117 | return NotFound(status);
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Tests.XUnit/OilServiceTest.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CleanArchitectureWebAPI.Application.Services;
3 | using CleanArchitectureWebAPI.Application.ViewModels.Oil;
4 | using CleanArchitectureWebAPI.Domian.Interfaces;
5 | using CleanArchitectureWebAPI.Domian.Models;
6 | using FluentAssertions;
7 | using Moq;
8 | using NUnit.Framework;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Text;
12 | using Xunit;
13 |
14 | namespace CleanArchitectureWebAPI.Tests.XUnit
15 | {
16 | public class OilServiceTest
17 | {
18 | [Fact]
19 | public void GetOils_NotExistingOils_ResultShouldBeEmpty()
20 | {
21 | // arrange
22 | var mockOilRepository = new Mock();
23 | var mockMapper = new Mock();
24 |
25 | // act
26 | var oilService = new OilService(mockOilRepository.Object, mockMapper.Object);
27 | var result = oilService.GetOils();
28 |
29 | // assert
30 | result.Oils.Should().BeEmpty();
31 | }
32 |
33 | [Fact]
34 | public void GetOils_NoExistingOils_ResultShouldBeOfTypeOilListViewModel()
35 | {
36 | // arrange
37 | var mockOilRepository = new Mock();
38 | var mockMapper = new Mock();
39 |
40 | // act
41 | var oilService = new OilService(mockOilRepository.Object, mockMapper.Object);
42 | var result = oilService.GetOils();
43 |
44 | // assert
45 | //Assert.IsType(result);
46 | result.Should().BeOfType(typeof(OilListViewModel));
47 | }
48 |
49 | [Fact]
50 | public void GetOilById_NoExistingOil_ResultShouldBeNull()
51 | {
52 | // arrange
53 | var mockOilRepository = new Mock();
54 | var mockMapper = new Mock();
55 |
56 | // act
57 | var oilService = new OilService(mockOilRepository.Object, mockMapper.Object);
58 | var result = oilService.GetOilById(Guid.NewGuid());
59 |
60 | //assert
61 | result.Should().BeNull();
62 | }
63 |
64 | [Fact]
65 | public void GetOilById_ExistingOil_ShouldReturnOilViewModel()
66 | {
67 | // arrange
68 | var mockOilRepository = new Mock();
69 | var oil = new Oil()
70 | {
71 | Id = Guid.NewGuid(),
72 | Brand = "New Oil",
73 | Description = "New Description",
74 | Scent = "New Scent",
75 | LiquidVolume = 22,
76 | UnitPrice = 20,
77 | UnitQuantity = 100,
78 | URL = "img/oil.jpg"
79 | };
80 | mockOilRepository.Setup(x => x.GetById(It.IsAny())).Returns(oil);
81 |
82 | var config = new MapperConfiguration(cfg =>
83 | {
84 | cfg.CreateMap();
85 | });
86 | var mapper = config.CreateMapper();
87 |
88 | var oilService = new OilService(mockOilRepository.Object, mapper);
89 |
90 | // act
91 | var result = oilService.GetOilById(Guid.NewGuid());
92 |
93 | // assert
94 | result.Should().BeOfType();
95 | }
96 |
97 |
98 | [Fact]
99 | public void GetOils_ExistingOils_ResultShouldBeTwoOils()
100 | {
101 | // arrange
102 | var mockOilRepository = new Mock();
103 |
104 | var mockedOils = new List
105 | {
106 | new Oil
107 | {
108 | Id = Guid.NewGuid(),
109 | Brand = "New Oil 1",
110 | Description = "New Description 1",
111 | Scent = "New Scent 1",
112 | LiquidVolume = 22,
113 | UnitPrice = 20,
114 | UnitQuantity = 100,
115 | URL = "img/oil1.jpg"
116 | },
117 | new Oil
118 | {
119 | Id = Guid.NewGuid(),
120 | Brand = "New Oil 2",
121 | Description = "New Description 2",
122 | Scent = "New Scent 2",
123 | LiquidVolume = 21,
124 | UnitPrice = 21,
125 | UnitQuantity = 100,
126 | URL = "img/oil2.jpg"
127 | },
128 | };
129 |
130 | mockOilRepository.Setup(x => x.GetAll()).Returns(mockedOils);
131 |
132 | var config = new MapperConfiguration(cfg =>
133 | {
134 | cfg.CreateMap();
135 | });
136 | var mapper = config.CreateMapper();
137 |
138 | // act
139 | var oilService = new OilService(mockOilRepository.Object, mapper);
140 | var result = oilService.GetOils();
141 |
142 | // assert
143 | result.Oils.Should().HaveCount(2);
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Controllers/SoapsController.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.Interfaces;
2 | using CleanArchitectureWebAPI.Application.ViewModels.Soap;
3 | using Microsoft.AspNetCore.Authentication.JwtBearer;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.Formatters;
8 | using Microsoft.Extensions.Caching.Memory;
9 | using NSwag.Annotations;
10 | using System;
11 | using System.Collections.Generic;
12 | using System.Linq;
13 | using System.Net;
14 | using System.Threading.Tasks;
15 |
16 | namespace CleanArchitectureWebAPI.WebAPI.Controllers
17 | {
18 | [Route("api/[controller]")]
19 | [OpenApiTag("Soaps", Description = "Methods to work with Soaps")]
20 | [ApiController]
21 | public class SoapsController : Controller
22 | {
23 | private readonly ISoapService _soapService;
24 | private string _allSoapsKey = "All_Soaps_Cache";
25 | private IMemoryCache _memoryCache;
26 | public SoapsController(ISoapService soapService, IMemoryCache memoryCache)
27 | {
28 | _soapService = soapService;
29 | _memoryCache = memoryCache;
30 | }
31 |
32 | [HttpGet] //URL/api/soaps http metod Get
33 | [SwaggerResponse(HttpStatusCode.OK, typeof(SoapListViewModel), Description = "Successfully Returned List Of Soaps")]
34 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "List Of Soaps Is Empty")]
35 | public IActionResult GetAll()
36 | {
37 | SoapListViewModel soapListViewModel;
38 | soapListViewModel = (SoapListViewModel)_memoryCache.Get(_allSoapsKey);
39 |
40 | if (soapListViewModel == null)
41 | {
42 | soapListViewModel = _soapService.GetSoaps();
43 |
44 | _memoryCache.Set(_allSoapsKey, soapListViewModel, new MemoryCacheEntryOptions()
45 | .SetSlidingExpiration(TimeSpan.FromMinutes(15))
46 | .SetAbsoluteExpiration(TimeSpan.FromHours(1)));
47 | }
48 |
49 | if (soapListViewModel != null)
50 | {
51 | return Ok(soapListViewModel);
52 | }
53 | else
54 | {
55 | return NotFound();
56 | }
57 |
58 | }
59 |
60 |
61 | [HttpGet("{id}")] //URL/api/soaps/id http metod Get
62 | [ResponseCache(Duration = 300, VaryByQueryKeys = new string[] { "id" })]
63 | [SwaggerResponse(HttpStatusCode.OK, typeof(SoapViewModel), Description = "Successfully Returned Soap Model")]
64 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "There Is No Soap Model With That Id")]
65 | public IActionResult GetById(Guid id)
66 | {
67 | var soap = _soapService.GetSoapById(id);
68 | if (soap != null)
69 | {
70 | return Ok(soap);
71 | }
72 | else
73 | {
74 | return NotFound(id);
75 | }
76 | }
77 |
78 |
79 | [HttpPost] //URL/api/soaps http metod Post
80 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
81 | [SwaggerResponse(HttpStatusCode.Unauthorized, null, Description = "You Don't Have Authorization For This Request")]
82 | [SwaggerResponse(HttpStatusCode.OK, typeof(SoapViewModel), Description = "Ok")]
83 | [SwaggerResponse(HttpStatusCode.Created, typeof(SoapViewModel), Description = "Soap Model Created")]
84 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "Soap Model Not Found")]
85 | public IActionResult AddOrEdit(SoapViewModel model)
86 | {
87 | if (model.Id == Guid.Empty)
88 | {
89 | model = _soapService.AddSoap(model);
90 | }
91 | else
92 | {
93 | _soapService.EditSoap(model);
94 | }
95 |
96 | _memoryCache.Remove(_allSoapsKey);
97 |
98 | return Ok(model);
99 | }
100 |
101 | [HttpDelete("{id}")] //URL/api/soaps/id http metod Delete
102 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
103 | [SwaggerResponse(HttpStatusCode.OK, typeof(SoapViewModel), Description = "Successfully Deleted Soap Model")]
104 | [SwaggerResponse(HttpStatusCode.NotFound, typeof(Guid), Description = "Soap Model You Want To Delete Doesn't Exist")]
105 | [SwaggerResponse(HttpStatusCode.Unauthorized, null, Description = "You Don't Have Authorization For This Request")]
106 | public IActionResult Delete(Guid id)
107 | {
108 | var soap = _soapService.GetSoapById(id);
109 | bool status = false; ;
110 |
111 | if (soap != null)
112 | {
113 | status = _soapService.DeleteSoap(id);
114 | _memoryCache.Remove(_allSoapsKey);
115 | return Ok(status);
116 | }
117 | else
118 | {
119 | return NotFound(status);
120 | }
121 | }
122 |
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30907.101
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application", "{44FBF3FE-EEFE-4B4E-A779-7C8CB33A56C1}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Domian", "Domian", "{01F77FD8-02E1-41CC-8735-857F46F70681}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{FF9B442E-FC6F-4566-AC3A-69A9F574E41D}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebAPI", "WebAPI", "{7B4F42BB-99A7-4E21-BCC2-A2DE841F127B}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitectureWebAPI.WebAPI", "CleanArchitectureWebAPI.WebAPI\CleanArchitectureWebAPI.WebAPI.csproj", "{5284677E-3D82-4AD0-B5F2-710B3EFA5313}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitectureWebAPI.Infrastructure.Data", "CleanArchitectureWebAPI.Infrastructure.Data\CleanArchitectureWebAPI.Infrastructure.Data.csproj", "{76037064-9C31-4CCB-8A95-749517BB9A47}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitectureWebAPI.Infrastructure.IoC", "CleanArchitectureWebAPI.Infrastructure.IoC\CleanArchitectureWebAPI.Infrastructure.IoC.csproj", "{4B1438E0-C768-4A27-ADD8-9DD247B2CF84}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitectureWebAPI.Domian", "CleanArchitectureWebAPI.Domian\CleanArchitectureWebAPI.Domian.csproj", "{ED3F24D1-F09A-42EF-9EFD-1884BBD0F122}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanArchitectureWebAPI.Application", "CleanArchitectureWebAPI.Application\CleanArchitectureWebAPI.Application.csproj", "{79B0F519-43E4-4962-8243-929861DF49B7}"
23 | EndProject
24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{36489C91-C21F-4E30-BFF9-CC57A4C8E4D0}"
25 | EndProject
26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitectureWebAPI.Tests.XUnit", "CleanArchitectureWebAPI.Tests.XUnit\CleanArchitectureWebAPI.Tests.XUnit.csproj", "{4F2C1360-F483-4295-9430-05975111B743}"
27 | EndProject
28 | Global
29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
30 | Debug|Any CPU = Debug|Any CPU
31 | Release|Any CPU = Release|Any CPU
32 | EndGlobalSection
33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
34 | {5284677E-3D82-4AD0-B5F2-710B3EFA5313}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {5284677E-3D82-4AD0-B5F2-710B3EFA5313}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {5284677E-3D82-4AD0-B5F2-710B3EFA5313}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {5284677E-3D82-4AD0-B5F2-710B3EFA5313}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {76037064-9C31-4CCB-8A95-749517BB9A47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {76037064-9C31-4CCB-8A95-749517BB9A47}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {76037064-9C31-4CCB-8A95-749517BB9A47}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {76037064-9C31-4CCB-8A95-749517BB9A47}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {4B1438E0-C768-4A27-ADD8-9DD247B2CF84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {4B1438E0-C768-4A27-ADD8-9DD247B2CF84}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {4B1438E0-C768-4A27-ADD8-9DD247B2CF84}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {4B1438E0-C768-4A27-ADD8-9DD247B2CF84}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {ED3F24D1-F09A-42EF-9EFD-1884BBD0F122}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {ED3F24D1-F09A-42EF-9EFD-1884BBD0F122}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {ED3F24D1-F09A-42EF-9EFD-1884BBD0F122}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 | {ED3F24D1-F09A-42EF-9EFD-1884BBD0F122}.Release|Any CPU.Build.0 = Release|Any CPU
50 | {79B0F519-43E4-4962-8243-929861DF49B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {79B0F519-43E4-4962-8243-929861DF49B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {79B0F519-43E4-4962-8243-929861DF49B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {79B0F519-43E4-4962-8243-929861DF49B7}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {4F2C1360-F483-4295-9430-05975111B743}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 | {4F2C1360-F483-4295-9430-05975111B743}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 | {4F2C1360-F483-4295-9430-05975111B743}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 | {4F2C1360-F483-4295-9430-05975111B743}.Release|Any CPU.Build.0 = Release|Any CPU
58 | EndGlobalSection
59 | GlobalSection(SolutionProperties) = preSolution
60 | HideSolutionNode = FALSE
61 | EndGlobalSection
62 | GlobalSection(NestedProjects) = preSolution
63 | {5284677E-3D82-4AD0-B5F2-710B3EFA5313} = {7B4F42BB-99A7-4E21-BCC2-A2DE841F127B}
64 | {76037064-9C31-4CCB-8A95-749517BB9A47} = {FF9B442E-FC6F-4566-AC3A-69A9F574E41D}
65 | {4B1438E0-C768-4A27-ADD8-9DD247B2CF84} = {FF9B442E-FC6F-4566-AC3A-69A9F574E41D}
66 | {ED3F24D1-F09A-42EF-9EFD-1884BBD0F122} = {01F77FD8-02E1-41CC-8735-857F46F70681}
67 | {79B0F519-43E4-4962-8243-929861DF49B7} = {44FBF3FE-EEFE-4B4E-A779-7C8CB33A56C1}
68 | {4F2C1360-F483-4295-9430-05975111B743} = {36489C91-C21F-4E30-BFF9-CC57A4C8E4D0}
69 | EndGlobalSection
70 | GlobalSection(ExtensibilityGlobals) = postSolution
71 | SolutionGuid = {4C91D554-0D0B-410D-818E-6064D8C4991E}
72 | EndGlobalSection
73 | EndGlobal
74 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Controllers/AccountController.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.WebAPI.ViewModels;
2 | using Microsoft.AspNetCore.Authorization;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.AspNetCore.Identity;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.IdentityModel.Tokens;
7 | using NSwag.Annotations;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.IdentityModel.Tokens.Jwt;
11 | using System.Linq;
12 | using System.Net;
13 | using System.Security.Claims;
14 | using System.Text;
15 | using System.Threading.Tasks;
16 |
17 | namespace CleanArchitectureWebAPI.WebAPI.Controllers
18 | {
19 | [Route("api/[controller]")]
20 | [OpenApiTag("Account", Description = "Methods to work with Account")]
21 | [ApiController]
22 | [AllowAnonymous]
23 | public class AccountController : Controller
24 | {
25 | UserManager _userManager;
26 | SignInManager _signInManager;
27 | RoleManager _roleManager;
28 |
29 | public AccountController(UserManager userManager, SignInManager signInManager, RoleManager roleManager)
30 | {
31 | _userManager = userManager;
32 | _signInManager = signInManager;
33 | _roleManager = roleManager;
34 | }
35 |
36 | [HttpPost("register")]
37 | [SwaggerResponse(HttpStatusCode.BadRequest, null, Description = "Registration Form Input Is Not Correct")]
38 | [SwaggerResponse(HttpStatusCode.OK, typeof(RegisterViewModel), Description = "Valid Registration")]
39 | public async Task> Register(RegisterViewModel model)
40 | {
41 | IdentityUser user = new IdentityUser()
42 | {
43 | UserName = model.UserName,
44 | Email = model.Email
45 | };
46 |
47 | IdentityResult userResult = await _userManager.CreateAsync(user, model.Password);
48 |
49 | if (userResult.Succeeded)
50 | {
51 | bool roleExcist = await _roleManager.RoleExistsAsync("User");
52 | if (!roleExcist)
53 | {
54 | await AddRole("User");
55 | }
56 |
57 | var roleResult = await _userManager.AddToRoleAsync(user, "User");
58 | if (roleResult.Succeeded)
59 | {
60 | return user;
61 | }
62 |
63 | foreach (var error in roleResult.Errors)
64 | {
65 | ModelState.AddModelError(error.Code, error.Description);
66 | }
67 | }
68 |
69 | foreach (var error in userResult.Errors)
70 | {
71 | ModelState.AddModelError(error.Code, error.Description);
72 | }
73 | return BadRequest(ModelState.Values);
74 | }
75 |
76 | [HttpPost("signIn")]
77 | [SwaggerResponse(HttpStatusCode.OK, typeof(SignInViewModel), Description = "Valid Sign In")]
78 | [SwaggerResponse(HttpStatusCode.BadRequest, null, Description = "User Not Found")]
79 | public async Task SignIn(SignInViewModel model)
80 | {
81 | var singInResult = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, false);
82 | if (singInResult.Succeeded)
83 | {
84 | var user = await _userManager.FindByNameAsync(model.UserName);
85 | var roles = await _userManager.GetRolesAsync(user);
86 |
87 | IdentityOptions identityOptions = new IdentityOptions();
88 | var claims = new Claim[]
89 | {
90 | new Claim(identityOptions.ClaimsIdentity.UserIdClaimType, user.Id),
91 | new Claim(identityOptions.ClaimsIdentity.UserNameClaimType, user.UserName),
92 | new Claim(identityOptions.ClaimsIdentity.RoleClaimType, roles[0])
93 | };
94 |
95 | var singingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("this-is-my-secret-key"));
96 | var signingCredentials = new SigningCredentials(singingKey, SecurityAlgorithms.HmacSha256);
97 | var token = new JwtSecurityToken(signingCredentials: signingCredentials,
98 | expires: DateTime.Now.AddHours(1), claims: claims);
99 |
100 | var obj = new
101 | {
102 | Token = new JwtSecurityTokenHandler().WriteToken(token),
103 | UserId = user.Id,
104 | UserName = user.UserName,
105 | Role = roles[0]
106 | };
107 |
108 | return Ok(obj);
109 | }
110 | return BadRequest(ModelState);
111 | }
112 |
113 |
114 | [HttpPost("signOut")]
115 | [SwaggerResponse(HttpStatusCode.NoContent, null, Description = "Valid Sign Out")]
116 | public async Task SignOut()
117 | {
118 | await _signInManager.SignOutAsync();
119 | return NoContent();
120 | }
121 |
122 |
123 | private async Task AddRole(string roleName)
124 | {
125 | var role = new IdentityRole();
126 | role.Name = roleName;
127 | return await _roleManager.CreateAsync(role);
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Logs/log20221129.txt:
--------------------------------------------------------------------------------
1 | 11/29/2022 23:09:48 +01:00 Application is starting
2 | 11/29/2022 23:09:55 +01:00 HTTP "GET" "/api/soaps" responded 200 in 724.6136 ms
3 | 11/29/2022 23:09:55 +01:00 HTTP "GET" "/favicon.ico" responded 404 in 4.7771 ms
4 | 11/29/2022 23:11:43 +01:00 Application is starting
5 | 11/29/2022 23:11:47 +01:00 HTTP "GET" "/api/soaps" responded 200 in 470.5643 ms
6 | 11/29/2022 23:12:05 +01:00 Application is starting
7 | 11/29/2022 23:12:08 +01:00 HTTP "GET" "/swagger" responded 302 in 84.6647 ms
8 | 11/29/2022 23:12:09 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 433.3172 ms
9 | 11/29/2022 23:12:09 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 47.8614 ms
10 | 11/29/2022 23:12:09 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 47.9248 ms
11 | 11/29/2022 23:12:09 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 65.3103 ms
12 | 11/29/2022 23:12:10 +01:00 HTTP "GET" "/swagger/favicon-32x32.png" responded 200 in 1.8156 ms
13 | 11/29/2022 23:12:10 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 970.1950 ms
14 | 11/29/2022 23:13:08 +01:00 HTTP "GET" "/api/balms" responded 200 in 429.8677 ms
15 | 11/29/2022 23:13:09 +01:00 HTTP "GET" "/api/soaps" responded 200 in 248.2927 ms
16 | 11/29/2022 23:13:13 +01:00 HTTP "GET" "/api/balms" responded 200 in 10.8398 ms
17 | 11/29/2022 23:13:17 +01:00 HTTP "GET" "/api/oils" responded 200 in 183.9310 ms
18 | 11/29/2022 23:13:50 +01:00 HTTP "OPTIONS" "/api/account/register" responded 204 in 19.8020 ms
19 | 11/29/2022 23:13:51 +01:00 HTTP "POST" "/api/account/register" responded 200 in 739.2508 ms
20 | 11/29/2022 23:13:51 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.9220 ms
21 | 11/29/2022 23:14:02 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.5237 ms
22 | 11/29/2022 23:14:03 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 436.5896 ms
23 | 11/29/2022 23:14:08 +01:00 HTTP "OPTIONS" "/api/account/signout" responded 204 in 1.2595 ms
24 | 11/29/2022 23:14:08 +01:00 HTTP "POST" "/api/account/signout" responded 204 in 116.0154 ms
25 | 11/29/2022 23:19:42 +01:00 HTTP "OPTIONS" "/api/account/register" responded 204 in 1.0883 ms
26 | 11/29/2022 23:19:42 +01:00 HTTP "POST" "/api/account/register" responded 200 in 219.2383 ms
27 | 11/29/2022 23:19:42 +01:00 HTTP "GET" "/api/soaps" responded 200 in 11.4109 ms
28 | 11/29/2022 23:21:29 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 12.6860 ms
29 | 11/29/2022 23:21:30 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 156.1264 ms
30 | 11/29/2022 23:21:34 +01:00 HTTP "GET" "/api/soaps" responded 200 in 3.6219 ms
31 | 11/29/2022 23:24:23 +01:00 HTTP "GET" "/api/soaps" responded 200 in 12.4562 ms
32 | 11/29/2022 23:24:26 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.8830 ms
33 | 11/29/2022 23:25:43 +01:00 HTTP "GET" "/api/soaps" responded 200 in 9.0596 ms
34 | 11/29/2022 23:28:14 +01:00 HTTP "GET" "/api/soaps" responded 200 in 12.1400 ms
35 | 11/29/2022 23:30:39 +01:00 HTTP "GET" "/api/soaps" responded 200 in 9.3043 ms
36 | 11/29/2022 23:33:12 +01:00 HTTP "GET" "/api/soaps" responded 200 in 14.3681 ms
37 | 11/29/2022 23:33:17 +01:00 HTTP "GET" "/api/soaps" responded 200 in 8.7317 ms
38 | 11/29/2022 23:33:57 +01:00 HTTP "GET" "/api/soaps" responded 200 in 5.5686 ms
39 | 11/29/2022 23:35:04 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 3.8506 ms
40 | 11/29/2022 23:35:05 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 128.4022 ms
41 | 11/29/2022 23:35:09 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.2158 ms
42 | 11/29/2022 23:37:45 +01:00 HTTP "GET" "/api/soaps" responded 200 in 13.9420 ms
43 | 11/29/2022 23:38:18 +01:00 HTTP "GET" "/api/soaps" responded 200 in 2.3003 ms
44 | 11/29/2022 23:38:31 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.4020 ms
45 | 11/29/2022 23:38:31 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 109.8202 ms
46 | 11/29/2022 23:38:36 +01:00 HTTP "GET" "/api/soaps" responded 200 in 2.5807 ms
47 | 11/29/2022 23:39:24 +01:00 HTTP "GET" "/api/soaps" responded 200 in 46.9351 ms
48 | 11/29/2022 23:39:32 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.6574 ms
49 | 11/29/2022 23:39:32 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 100.8753 ms
50 | 11/29/2022 23:39:34 +01:00 HTTP "GET" "/api/soaps" responded 200 in 14.7844 ms
51 | 11/29/2022 23:40:59 +01:00 HTTP "GET" "/api/soaps" responded 200 in 10.4308 ms
52 | 11/29/2022 23:41:18 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.4209 ms
53 | 11/29/2022 23:41:18 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 102.8204 ms
54 | 11/29/2022 23:41:20 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.3092 ms
55 | 11/29/2022 23:42:01 +01:00 HTTP "GET" "/api/soaps" responded 200 in 11.8909 ms
56 | 11/29/2022 23:42:14 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.2311 ms
57 | 11/29/2022 23:42:14 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 129.4010 ms
58 | 11/29/2022 23:42:16 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.3074 ms
59 | 11/29/2022 23:44:14 +01:00 HTTP "GET" "/api/soaps" responded 200 in 3.9691 ms
60 | 11/29/2022 23:44:29 +01:00 HTTP "GET" "/api/soaps" responded 200 in 6.7360 ms
61 | 11/29/2022 23:44:39 +01:00 HTTP "GET" "/api/soaps" responded 200 in 0.7411 ms
62 | 11/29/2022 23:44:49 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.4334 ms
63 | 11/29/2022 23:44:49 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 67.9731 ms
64 | 11/29/2022 23:44:56 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.8087 ms
65 | 11/29/2022 23:46:20 +01:00 HTTP "GET" "/api/soaps" responded 200 in 7.5476 ms
66 | 11/29/2022 23:46:28 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.8595 ms
67 | 11/29/2022 23:46:28 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 108.6035 ms
68 | 11/29/2022 23:46:29 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.6106 ms
69 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Controllers/BalmsController.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Application.Interfaces;
2 | using CleanArchitectureWebAPI.Application.ViewModels.Balm;
3 | using Microsoft.AspNetCore.Authentication.JwtBearer;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.Extensions.Caching.Memory;
8 | using NSwag.Annotations;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Net;
13 | using System.Threading.Tasks;
14 |
15 | namespace CleanArchitectureWebAPI.WebAPI.Controllers
16 | {
17 | [Route("api/[controller]")]
18 | [OpenApiTag("Balms", Description = "Methods to work with Balms")]
19 | [ApiController]
20 | public class BalmsController : Controller
21 | {
22 | private readonly IBalmService _balmService;
23 | private IMemoryCache _memoryCache;
24 | private string _allBalmsKey = "All_Balms_Cache";
25 | public BalmsController(IBalmService balmService, IMemoryCache memoryCache)
26 | {
27 | _balmService = balmService;
28 | _memoryCache = memoryCache;
29 | }
30 |
31 |
32 | [HttpGet] //URL/api/balms http metod Get
33 | [SwaggerResponse(HttpStatusCode.OK, typeof(BalmListViewModel), Description = "Successfully Returned List Of Balms")]
34 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "List Of Balms Is Empty")]
35 | public IActionResult GetAll()
36 | {
37 | BalmListViewModel balmListViewModel;
38 |
39 | // Returns an entry from the cache
40 | balmListViewModel = (BalmListViewModel)_memoryCache.Get(_allBalmsKey);
41 |
42 | // If there is no entry in the cache for that key, go to database
43 | if (balmListViewModel == null)
44 | {
45 | balmListViewModel = _balmService.GetBalms();
46 | // Setting cache entry for that key.
47 | _memoryCache.Set(_allBalmsKey, balmListViewModel, new MemoryCacheEntryOptions().
48 | SetSlidingExpiration(TimeSpan.FromMinutes(15)).
49 | SetAbsoluteExpiration(TimeSpan.FromHours(1)));
50 | // SetSlidingExpiration Sets how long the cache entry can be inactive
51 | // SetAbsoluteExpiration Sets an absolute expiration date for the cache entry
52 |
53 | // THIS IS JUST TO DEMONSTRATE HOW CACHE WORKS. YOU CAN ALLWAYS CHANGE THE TIMESPAN
54 | }
55 |
56 | if (balmListViewModel != null)
57 | {
58 | return Ok(balmListViewModel);
59 | }
60 | else
61 | {
62 | return NotFound();
63 | }
64 | }
65 |
66 |
67 | [HttpGet("{id}")] //URL/api/balms/id http metod Get
68 | [SwaggerResponse(HttpStatusCode.OK, typeof(BalmViewModel), Description = "Successfully Returned Balm Model")]
69 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "There Is No Balm Model With That Id")]
70 | [ResponseCache(Duration = 300, VaryByQueryKeys = new string[] { "id" })] // This is the Response Cache
71 | public IActionResult GetById(Guid id)
72 | {
73 | var balm = _balmService.GetBalmById(id);
74 | if (balm != null)
75 | {
76 | return Ok(balm);
77 | }
78 | else
79 | {
80 | return NotFound(id);
81 | }
82 |
83 | }
84 |
85 |
86 |
87 | [HttpPost] //URL/api/balms http metod Post
88 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
89 | [SwaggerResponse(HttpStatusCode.Unauthorized, null, Description = "You Don't Have Authorization For This Request")]
90 | [SwaggerResponse(HttpStatusCode.OK, typeof(BalmViewModel), Description = "Ok")]
91 | [SwaggerResponse(HttpStatusCode.Created, typeof(BalmViewModel), Description = "Balm Model Created")]
92 | [SwaggerResponse(HttpStatusCode.NotFound, null, Description = "Balm Model Not Found")]
93 | public IActionResult AddOrEdit(BalmViewModel model)
94 | {
95 | if (model.Id == Guid.Empty)
96 | {
97 | model = _balmService.AddBalm(model);
98 | }
99 | else
100 | {
101 | _balmService.EditBalm(model);
102 | }
103 |
104 | _memoryCache.Remove(_allBalmsKey);
105 |
106 | return Ok(model);
107 | }
108 |
109 |
110 |
111 | [HttpDelete("{id}")] //URL/api/balms/id http metod Delete
112 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin")]
113 | [SwaggerResponse(HttpStatusCode.OK, typeof(BalmViewModel), Description = "Successfully Deleted Balm Model")]
114 | [SwaggerResponse(HttpStatusCode.NotFound, typeof(Guid), Description = "Balm Model You Want To Delete Doesn't Exist")]
115 | [SwaggerResponse(HttpStatusCode.Unauthorized, null, Description = "You Don't Have Authorization For This Request")]
116 | public IActionResult Delete(Guid id)
117 | {
118 | var balm = _balmService.GetBalmById(id);
119 | var status = false;
120 | if (balm != null)
121 | {
122 | status = _balmService.DeleteBalm(id);
123 | _memoryCache.Remove(_allBalmsKey);
124 | return Ok(balm);
125 | }
126 | else
127 | {
128 | return NotFound(status);
129 | }
130 |
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clean Architecture ASP.NET Core Web API 3.1
2 |
3 | ## Project Use Case
4 |
5 | This is a WebAPI Project, implemented with Clean Architecture pattern. The code is very clean and it is very manageable.
6 | You can add your own models, validations, logic, etc. You can also create MVC project on top of it, or just
7 | use it as a api, deployed locally or publish on some server and use some front-end framework to show the data.
8 |
9 | The front-end for this project is available on GitHub, and can be found [here](https://github.com/viktor1vasilevski/beard-products-ui).
10 |
11 |
12 | ## Installation
13 |
14 | 1. Download the code - Clone the repository or download the zip file
15 |
16 | 2. Change the connection string - In the WebAPI project in the ```appsettings.json``` file enter your server name
17 | and name of the database(I was working on SQL Server 2014 Management Studio).
18 |
19 | 3. Go into the Package Manager Console and type: ```Add-Migration "Init```"
20 |
21 |
22 | ***!IMPORTANT - Make sure that your WebAPI is Set as Startup Project, and in the Package Manager Console your Default project is CleanArchitectureWebAPI.Infrastructure.Data selected.***
23 | - this will create folder Migrations in CleanArchitectureWebAPI.Infrastructure.Data with the migration.
24 |
25 | 4. When this is done, just type in the Package Manager Console: ```Update-Database```
26 | - this will create the database with you database name with all the ASP.NET Identity tables and the models.
27 |
28 | 5. Run the project
29 | - this will build the project and it will seed the database with some data, just something to work with.
30 | (maybe you have to close the SQL Server 2014 Management Studio and start it again)
31 | - in the WebAPI project it will also be created folder Log with two files. One .json file and other .txt.
32 | These are for detail logging information.
33 |
34 | 6. Next step is that you have to create "Admin" to take full experience of the API.
35 | - because the API works with roles "Admin" and "User", you have to go first to create roles "Admin" and "User" in dbo.AspNetRoles.
36 | - you can use this set of commands in SQL Server 2014 Management Studio to create the roles "Admin" and "User".
37 |
38 | ```sql
39 | USE [yourDatabaseName]
40 | GO
41 | INSERT INTO dbo.AspNetRoles(Id, Name, NormalizedName, ConcurrencyStamp)
42 | VALUES (1, 'Admin', 'ADMIN', null)
43 | INSERT INTO dbo.AspNetRoles(Id, Name, NormalizedName, ConcurrencyStamp)
44 | VALUES (2, 'User', 'USER', null)
45 | ```
46 |
47 | 7. Next step is to register some user
48 | - ***from this point on, your api/project should be running all the time during the register, login and CRUD operations***
49 | - ***I hosted the api/project locally, on my computer***
50 | - you can use Postman or Fiddler for registration, or some other program, and provide this json format in the body with your own values.
51 | (I used Postman)
52 | - The method is ```Post``` and the url will be ```localhost:port/api/account/register```
53 |
54 | ```json
55 | {
56 | "Username": "YourUsername",
57 | "Email": "YourEmail@yahoo.com",
58 | "Password": "YourPassword@123",
59 | "ConfirmPassword": "YourPassword@123"
60 | }
61 | ```
62 | - after you get registered, automatically, this user will have the role "User".
63 | Go in the SQL Server 2014 Management Studio, your database, table AspNetUserRoles and notice that the RoleId is already 2 for that one user.
64 | Just edit the table and change it manually to 1 and press TAB,
65 | or write this command in SQL
66 |
67 | ```sql
68 | USE [yourDatabaseName]
69 | GO
70 | UPDATE [dbo].[AspNetUserRoles] SET RoleId = 1
71 | WHERE UserId = 'userId' --here enter the user ID
72 | ```
73 | This user, from then on will be with role of "Admin".
74 |
75 | 8. Next step is to Sign In some user
76 | - url: ```localhost/port/api/account/signin```, and this method is also ```Post``` and you should provide this json format into the body
77 | ```json
78 | {
79 | "Username": "YourUsername",
80 | "Password": "YourPassword@123"
81 | }
82 | ```
83 | the logging user will be successful and you will recive a JWT Token,
84 | which you must use for Authorization with the prefix Bearer : YourToken
85 | ```
86 | In Postman just select the Auth, and from the dropdown menu select Bearer Token
87 | ```
88 |
89 | Now the commands Post, Create, Edit and Delete will be available, otherwise you will get the:
90 | ```js
91 | 401 Unauthorized
92 | ```
93 | *(for Get and GetById you dont need no Authorization)*
94 |
95 |
96 | Every other registered user in the future will be with the role "User". To change it to "Admin" just do the step 7, part 5.
97 | (in the project there is no request that is authorized by "User", so you can use this below some other Http request).
98 | ```C#
99 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "User")]
100 | ```
101 | ## Principle, Patterns and external libraries used.
102 |
103 | 1. Clean (Onion) Architecture Project with Domain Driven Design and SOLID principles.
104 | 2. ASP.NET Core Dependency Injection
105 | 3. Repository Pattern
106 | 4. Unit Of Work Pattern
107 | 5. Automapper
108 | 6. Logging with Serilog
109 | 7. In Memory Caching
110 | 8. Response Caching
111 | 9. Swagger
112 | 10. JWT Token
113 | 11. Unit Testing with Moq
114 |
115 | ## ToDO
116 |
117 | 1. There is no business logic, since this is only a concept.
118 | 2. The enities don't have foreign keys or any common table
119 |
120 | ## Notes
121 | 1. This project is build somewhere in the beginning of 2021 and I was just starting to apply for a job. Now, that I changed few companies and I gained experience working on real projects, and think about the architecure of the project, I can see that the database, or the models, are not well structured.
122 | Because the api is build for a kind of web shop application, ultimatly what you want is to buy a product. Well now, as the architecture is build, it is possible but the solution is not good. Say you have build a front-end project with Angular and you have cart section, and in that cart you have one soap and one balm as a products.
123 | Now the next step is to have separate controller in the back-end that handles this logic. In this controller you have to have all the services because you don't know user what will pick, soap, balm or oil. And for every item, you have to check in each service if that id is in that table of the database. Because on the back-end, as the user checkouts, the user want to buy two items, and you will get two ids, nothing more. Those ids don't tell what is it. Is it a soap or balm. And imagine if you have hundred products. For instance hand soaps, scissors, shampoos and what not. The project will be difficult to manage and in this controller where the buying is made, you will end up with hundred servers, or instances from the tables of the database, and when the user wants to buy just one product, you have to search in all the tables just to find that one product which the user wants to buy. Or even if you send from front end some information about the product, you have to have if statements or switch logic. At the very end I think it's a bad way of dealing with this kind of logic.
124 |
125 | The solution for this problem is to have one table, named Products, and in this table you will have the columns like Name, Brand, Edition, Unit Price and so on.
126 | Or even better you will have another table Categories, and in that table you will save the categories like, beard category, hair category, hands category, and in this
127 | Products table you will have CategoryId, respectively for every product. This way all the products are in one table and your search will be only in this table, that's it. The buying of products will be much easier and no matter the product, you will recive array of ids and the search will be done only in one table. This project can be even more improved, for instance you can write the logic for creating generic unit of work and generic repositories pattern, you can have another table Product Details where you will store the details for each product, you can insert column in Product table named ProductDetailsJson where all the details from the product you will store as a json file and on the front end you will handle this object, and so on. Hope this helps a little bit.
128 |
129 | 2. The solution folder Domian, should be named Domain. And also the name of the Class Library from that folder should be named CleanArchitectureWebAPI.Dom**ai**n, not CleanArchitectureWebAPI.Dom**ia**n.
130 |
131 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Context/LibraryDbContextSeed.cs:
--------------------------------------------------------------------------------
1 | using CleanArchitectureWebAPI.Domian.Models;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.Extensions.Logging;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Context
11 | {
12 | public class LibraryDbContextSeed
13 | {
14 | public static async Task SeedAsync(LibraryDbContext libraryDbContext, ILoggerFactory loggerFactory, int? retry = 0)
15 | {
16 | int retryForAvailability = retry.Value;
17 |
18 | try
19 | {
20 | // NOTE : Only run this if using a real database
21 | libraryDbContext.Database.Migrate();
22 | libraryDbContext.Database.EnsureCreated();
23 |
24 | // seed Soaps
25 | await SeedSoapsAsync(libraryDbContext);
26 |
27 | // seed Oils
28 | await SeedOilsAsync(libraryDbContext);
29 |
30 | //// seed Balms
31 | await SeedBalmsAsync(libraryDbContext);
32 |
33 | }
34 | catch (Exception exception)
35 | {
36 | if (retryForAvailability < 10)
37 | {
38 | retryForAvailability++;
39 | var log = loggerFactory.CreateLogger();
40 | log.LogError(exception.Message);
41 | await SeedAsync(libraryDbContext, loggerFactory, retryForAvailability);
42 | }
43 | throw;
44 | }
45 | }
46 |
47 | private static async Task SeedSoapsAsync(LibraryDbContext libraryDbContext)
48 | {
49 | if (libraryDbContext.Soaps.Any())
50 | return;
51 |
52 | var soaps = new List()
53 | {
54 | new Soap()
55 | {
56 | Edition = "Orange Brick",
57 | Brand = "Craftsman",
58 | Description = "With the bright, earthy smell of the forest and a hint of lavender," +
59 | " our beard soap delivers a refreshing start to your beard care regiment. " +
60 | "Made from a choice blend of oils, this bar is formulated to build a particularly thick, " +
61 | "creamy lather for a thorough beard cleaning that won't leave your beard or face dry",
62 | UnitPrice = 13,
63 | UnitQuantity = 100,
64 | URL = LibraryDbContextSeedData.ORANGE_BRICK,
65 | Created = DateTime.Now,
66 | LastModified = DateTime.Now,
67 | CreatedBy = "Viktor"
68 | },
69 |
70 | new Soap ()
71 | {
72 | Edition = "Brown",
73 | Brand = "Honest Amish",
74 | Description = "Exclusively Handmade Natural Skin Care Soap Bar for dry skin. " +
75 | "Beautiful handcrafted natural skin care soap bar from Organic Goat milk, " +
76 | "extra virgin organic Coconut, organic Palm and extra virgin Olive oils.",
77 | UnitPrice = 11,
78 | UnitQuantity = 100,
79 | URL = LibraryDbContextSeedData.BROWN,
80 | Created = DateTime.Now,
81 | LastModified = DateTime.Now,
82 | CreatedBy = "Viktor"
83 | },
84 |
85 | new Soap()
86 | {
87 | Edition = "Light Yellow",
88 | Brand = "Crocodile",
89 | Description = "Artisanal soap handmade in Ojai, California with locally farmed fresh raw " +
90 | "Goat Milk and Organic Essential Oils, Hand mixed, poured and cut into long lasting bars",
91 | UnitPrice = 12,
92 | UnitQuantity = 100,
93 | URL = LibraryDbContextSeedData.LIGHT_YELLOW,
94 | Created = DateTime.Now,
95 | LastModified = DateTime.Now,
96 | CreatedBy = "Viktor"
97 | }
98 | };
99 |
100 | libraryDbContext.Soaps.AddRange(soaps);
101 | await libraryDbContext.SaveChangesAsync();
102 | }
103 |
104 |
105 | private static async Task SeedOilsAsync(LibraryDbContext libraryDbContext)
106 | {
107 | if (libraryDbContext.Oils.Any())
108 | return;
109 |
110 | var oils = new List()
111 | {
112 | new Oil()
113 | {
114 | Scent = "Forest Woods",
115 | LiquidVolume = 30,
116 | Brand = "Hawkins&Brimble",
117 | Description = "Perfect for sensitive skin, Le Labo’s formula is entirely " +
118 | "plant-based (making it suitable for vegans). " +
119 | "The nonirritating elixir carries the comforting scent of lavender, " +
120 | "along with bergamot and tonka bean.",
121 | UnitPrice = 23,
122 | UnitQuantity = 100,
123 | URL = LibraryDbContextSeedData.FOREST_WOODS,
124 | Created = DateTime.Now,
125 | LastModified = DateTime.Now,
126 | CreatedBy = "Viktor"
127 | },
128 |
129 | new Oil()
130 | {
131 | Scent = "Cedar",
132 | LiquidVolume = 50,
133 | Brand = "Johnny's Boat",
134 | Description = "Our beard oil comes equipped with a nifty ball rod " +
135 | "cap so that you can take a little or a lot depending on your " +
136 | "beard size. Just add a few drops to your fingertips and massage the " +
137 | "oils through your beard and mustache hair",
138 | UnitPrice = 28,
139 | UnitQuantity = 100,
140 | URL = LibraryDbContextSeedData.CEDAR,
141 | Created = DateTime.Now,
142 | LastModified = DateTime.Now,
143 | CreatedBy = "Viktor"
144 | },
145 |
146 | new Oil()
147 | {
148 | Scent = "Summer Breeze",
149 | LiquidVolume = 50,
150 | Brand = "Beardo",
151 | Description = "Handmade with 100% natural oils, this beard oil " +
152 | "absorbs quickly and gives your beard a smooth, soft, subtle shine. " +
153 | "Great for conditioning the skin underneath your beard. " +
154 | "Promotes hair growth by helping you maintain a healthy beard. " +
155 | "Timber - Mild Scent of Cedar & Fir NeedleMB BEARD OIL TIMBER",
156 | UnitPrice = 25,
157 | UnitQuantity = 100,
158 | URL = LibraryDbContextSeedData.SUMMER_BREEZE,
159 | Created = DateTime.Now,
160 | LastModified = DateTime.Now,
161 | CreatedBy = "Viktor"
162 | }
163 | };
164 |
165 | libraryDbContext.Oils.AddRange(oils);
166 | await libraryDbContext.SaveChangesAsync();
167 | }
168 |
169 | private static async Task SeedBalmsAsync(LibraryDbContext libraryDbContext)
170 | {
171 | if (libraryDbContext.Balms.Any())
172 | return;
173 |
174 | var balms = new List()
175 | {
176 | new Balm()
177 | {
178 | Volume = 70,
179 | Brand = "Honest Amish",
180 | Description = "The best for your beard we guarantee it. " +
181 | "Honest Amish Beard Balm is created from the finest organic " +
182 | "ingredients available. We start with a proprietary blend of hair " +
183 | "strengthening botanical infused in a base of Virgin Argan, Avocado, " +
184 | "Almond, Virgin Pumpkin Seed, and Apricot Kernel Oils",
185 | UnitPrice = 21,
186 | UnitQuantity = 100,
187 | URL = LibraryDbContextSeedData.HONEST_AMISH,
188 | Created = DateTime.Now,
189 | LastModified = DateTime.Now,
190 | CreatedBy = "Viktor"
191 | },
192 |
193 | new Balm ()
194 | {
195 | Volume = 50,
196 | Brand = "Rocky Mountain",
197 | Description = "The natural, organic ingredients get deep into the hair and nourishes " +
198 | "it from inside and out! It works on long and short beards to condition & soften " +
199 | "your hair, improve strength & shine",
200 | UnitPrice = 19,
201 | UnitQuantity = 100,
202 | URL = LibraryDbContextSeedData.ROCKY_MOUNTAIN,
203 | Created = DateTime.Now,
204 | LastModified = DateTime.Now,
205 | CreatedBy = "Viktor"
206 | },
207 |
208 | new Balm()
209 | {
210 | Volume = 100,
211 | Brand = "Olympus",
212 | Description = "All Natural Ingredients - Featuring over eight essential oils, " +
213 | "we are proud to say that our product is all-natural with ingredients that will " +
214 | "leave your beard feeling and looking amazing",
215 | UnitPrice = 20,
216 | UnitQuantity = 100,
217 | URL = LibraryDbContextSeedData.OLYMPUS,
218 | Created = DateTime.Now,
219 | LastModified = DateTime.Now,
220 | CreatedBy = "Viktor"
221 | }
222 | };
223 |
224 | libraryDbContext.Balms.AddRange(balms);
225 | await libraryDbContext.SaveChangesAsync();
226 | }
227 | }
228 |
229 |
230 | }
231 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.WebAPI/Logs/log20221209.txt:
--------------------------------------------------------------------------------
1 | 12/09/2022 00:00:11 +01:00 HTTP "GET" "/api/soaps" responded 200 in 10.1264 ms
2 | 12/09/2022 00:00:51 +01:00 HTTP "GET" "/api/soaps" responded 200 in 5.4816 ms
3 | 12/09/2022 00:01:01 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.1286 ms
4 | 12/09/2022 00:01:01 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 57.7227 ms
5 | 12/09/2022 00:01:02 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.0586 ms
6 | 12/09/2022 00:01:27 +01:00 HTTP "GET" "/api/soaps" responded 200 in 11.6115 ms
7 | 12/09/2022 00:01:29 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.1617 ms
8 | 12/09/2022 00:01:29 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 98.0712 ms
9 | 12/09/2022 00:01:30 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.0933 ms
10 | 12/09/2022 00:02:07 +01:00 HTTP "GET" "/api/soaps" responded 200 in 0.5965 ms
11 | 12/09/2022 00:02:27 +01:00 HTTP "GET" "/api/soaps" responded 200 in 5.4775 ms
12 | 12/09/2022 00:02:32 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.1021 ms
13 | 12/09/2022 00:02:32 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 96.9925 ms
14 | 12/09/2022 00:02:32 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.5075 ms
15 | 12/09/2022 00:03:04 +01:00 HTTP "GET" "/api/soaps" responded 200 in 0.6631 ms
16 | 12/09/2022 00:03:06 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.4180 ms
17 | 12/09/2022 00:03:06 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 101.6925 ms
18 | 12/09/2022 00:03:07 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.3358 ms
19 | 12/09/2022 00:03:30 +01:00 HTTP "GET" "/api/soaps" responded 200 in 10.1174 ms
20 | 12/09/2022 00:04:30 +01:00 HTTP "GET" "/api/soaps" responded 200 in 2.2093 ms
21 | 12/09/2022 00:04:32 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.5478 ms
22 | 12/09/2022 00:04:32 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 52.2908 ms
23 | 12/09/2022 00:04:32 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.0387 ms
24 | 12/09/2022 00:05:24 +01:00 HTTP "GET" "/api/soaps" responded 200 in 2.4310 ms
25 | 12/09/2022 00:10:45 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.1330 ms
26 | 12/09/2022 00:10:45 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 189.3405 ms
27 | 12/09/2022 00:10:45 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.8746 ms
28 | 12/09/2022 00:11:52 +01:00 HTTP "GET" "/api/soaps" responded 200 in 2.6648 ms
29 | 12/09/2022 00:12:11 +01:00 HTTP "GET" "/api/soaps" responded 200 in 5.2471 ms
30 | 12/09/2022 00:12:19 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.2020 ms
31 | 12/09/2022 00:12:19 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 26.9546 ms
32 | 12/09/2022 00:12:20 +01:00 HTTP "GET" "/api/soaps" responded 200 in 0.8205 ms
33 | 12/09/2022 00:14:33 +01:00 HTTP "GET" "/api/soaps" responded 200 in 15.3233 ms
34 | 12/09/2022 00:20:20 +01:00 HTTP "GET" "/api/soaps" responded 200 in 2.4819 ms
35 | 12/09/2022 00:20:22 +01:00 HTTP "GET" "/api/soaps" responded 200 in 5.4616 ms
36 | 12/09/2022 00:30:04 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.2252 ms
37 | 12/09/2022 00:30:04 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 198.3786 ms
38 | 12/09/2022 00:30:05 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.2145 ms
39 | 12/09/2022 00:30:27 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.0599 ms
40 | 12/09/2022 00:31:07 +01:00 HTTP "GET" "/api/soaps" responded 200 in 3.3731 ms
41 | 12/09/2022 00:31:10 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.5877 ms
42 | 12/09/2022 00:31:10 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 121.1429 ms
43 | 12/09/2022 00:31:16 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.5882 ms
44 | 12/09/2022 00:31:56 +01:00 HTTP "GET" "/api/soaps" responded 200 in 0.7513 ms
45 | 12/09/2022 00:31:58 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 0.1395 ms
46 | 12/09/2022 00:31:58 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 59.9206 ms
47 | 12/09/2022 00:31:59 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.8714 ms
48 | 12/09/2022 00:32:42 +01:00 HTTP "GET" "/api/soaps" responded 200 in 9.4609 ms
49 | 12/09/2022 00:34:44 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.1062 ms
50 | 12/09/2022 00:35:07 +01:00 HTTP "GET" "/api/soaps" responded 200 in 0.8878 ms
51 | 12/09/2022 00:36:25 +01:00 HTTP "GET" "/api/soaps" responded 200 in 7.2185 ms
52 | 12/09/2022 00:41:23 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.2597 ms
53 | 12/09/2022 00:43:08 +01:00 HTTP "GET" "/api/soaps" responded 200 in 1.0102 ms
54 | 12/09/2022 00:44:01 +01:00 HTTP "GET" "/swagger/[object%20Module]" responded 404 in 3.0251 ms
55 | 12/09/2022 00:45:00 +01:00 HTTP "POST" "/api/Soaps" responded 401 in 44.5475 ms
56 | 12/09/2022 00:47:16 +01:00 Application is starting
57 | 12/09/2022 00:47:19 +01:00 HTTP "GET" "/swagger" responded 302 in 81.3948 ms
58 | 12/09/2022 00:47:20 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 294.4948 ms
59 | 12/09/2022 00:47:20 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 69.7981 ms
60 | 12/09/2022 00:47:20 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 69.9286 ms
61 | 12/09/2022 00:47:20 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 70.3680 ms
62 | 12/09/2022 00:47:21 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 928.6946 ms
63 | 12/09/2022 00:47:38 +01:00 HTTP "GET" "/swagger/[object%20Module]" responded 404 in 3.5914 ms
64 | 12/09/2022 00:48:28 +01:00 Application is starting
65 | 12/09/2022 00:48:31 +01:00 HTTP "GET" "/swagger" responded 302 in 77.6278 ms
66 | 12/09/2022 00:48:31 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 300.3175 ms
67 | 12/09/2022 00:48:31 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 45.6879 ms
68 | 12/09/2022 00:48:31 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 45.1633 ms
69 | 12/09/2022 00:48:31 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 45.6820 ms
70 | 12/09/2022 00:48:32 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 808.8700 ms
71 | 12/09/2022 00:49:31 +01:00 HTTP "GET" "/swagger/[object%20Module]" responded 404 in 5.3797 ms
72 | 12/09/2022 00:50:01 +01:00 HTTP "POST" "/api/account/signin" responded 415 in 201.8797 ms
73 | 12/09/2022 00:50:15 +01:00 HTTP "POST" "/api/account/signin" responded 415 in 18.1274 ms
74 | 12/09/2022 00:51:55 +01:00 HTTP "GET" "/api/soaps" responded 200 in 4124.0604 ms
75 | 12/09/2022 00:52:27 +01:00 HTTP "POST" "/api/account/signin" responded 415 in 20.1832 ms
76 | 12/09/2022 00:52:43 +01:00 HTTP "POST" "/api/account/signin" responded 400 in 26.1706 ms
77 | 12/09/2022 00:53:02 +01:00 HTTP "POST" "/api/account/signin?username=Viktor&password=Viktor@123" responded 400 in 6.1172 ms
78 | 12/09/2022 00:53:17 +01:00 HTTP "POST" "/api/account/signin?username=Viktor&password=Viktor@123" responded 400 in 8.7271 ms
79 | 12/09/2022 00:54:30 +01:00 HTTP "POST" "/api/account/signin?userName=Viktor&password=Viktor@123" responded 400 in 40.7582 ms
80 | 12/09/2022 00:55:18 +01:00 HTTP "POST" "/api/account/signin" responded 415 in 4.3326 ms
81 | 12/09/2022 00:55:56 +01:00 HTTP "POST" "/api/account/signin" responded 415 in 21.5124 ms
82 | 12/09/2022 00:57:15 +01:00 HTTP "POST" "/api/Account/signIn" responded 200 in 2398.2690 ms
83 | 12/09/2022 00:57:34 +01:00 HTTP "GET" "/swagger/[object%20Module]" responded 404 in 0.5008 ms
84 | 12/09/2022 01:01:02 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 4456.3782 ms
85 | 12/09/2022 01:02:43 +01:00 HTTP "GET" "/swagger/[object%20Module]" responded 404 in 4.2604 ms
86 | 12/09/2022 01:03:30 +01:00 HTTP "POST" "/api/soaps" responded 400 in 136.2441 ms
87 | 12/09/2022 01:04:09 +01:00 HTTP "POST" "/api/soaps" responded 400 in 17.8104 ms
88 | 12/09/2022 01:05:24 +01:00 HTTP "POST" "/api/soaps" responded 200 in 60500.1453 ms
89 | 12/09/2022 01:16:36 +01:00 Application is starting
90 | 12/09/2022 01:16:41 +01:00 HTTP "GET" "/swagger" responded 302 in 83.1426 ms
91 | 12/09/2022 01:16:41 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 280.4513 ms
92 | 12/09/2022 01:16:41 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 72.0183 ms
93 | 12/09/2022 01:16:41 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 72.0649 ms
94 | 12/09/2022 01:16:41 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 72.1175 ms
95 | 12/09/2022 01:16:42 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 761.2729 ms
96 | 12/09/2022 01:18:05 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 19.0850 ms
97 | 12/09/2022 01:18:16 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 10979.6440 ms
98 | 12/09/2022 01:18:22 +01:00 HTTP "GET" "/api/soaps" responded 200 in 4341.2447 ms
99 | 12/09/2022 01:18:26 +01:00 HTTP "OPTIONS" "/api/soaps/7680581a-25cf-434b-b40f-830d8c217ecf" responded 204 in 1.1519 ms
100 | 12/09/2022 01:18:27 +01:00 HTTP "DELETE" "/api/soaps/7680581a-25cf-434b-b40f-830d8c217ecf" responded 200 in 434.0389 ms
101 | 12/09/2022 01:18:28 +01:00 HTTP "OPTIONS" "/api/soaps/f14fcdce-daf6-498d-0920-08dad9693d5b" responded 204 in 1.0246 ms
102 | 12/09/2022 01:18:28 +01:00 HTTP "DELETE" "/api/soaps/f14fcdce-daf6-498d-0920-08dad9693d5b" responded 200 in 77.0524 ms
103 | 12/09/2022 01:18:30 +01:00 HTTP "OPTIONS" "/api/soaps/3944ab7e-5be6-4284-091f-08dad9693d5b" responded 204 in 1.1154 ms
104 | 12/09/2022 01:18:30 +01:00 HTTP "DELETE" "/api/soaps/3944ab7e-5be6-4284-091f-08dad9693d5b" responded 200 in 90.5854 ms
105 | 12/09/2022 01:18:31 +01:00 HTTP "OPTIONS" "/api/soaps/146ad7a3-12b6-4d25-091e-08dad9693d5b" responded 204 in 0.8602 ms
106 | 12/09/2022 01:18:32 +01:00 HTTP "DELETE" "/api/soaps/146ad7a3-12b6-4d25-091e-08dad9693d5b" responded 200 in 71.7995 ms
107 | 12/09/2022 01:18:45 +01:00 Application is starting
108 | 12/09/2022 01:18:49 +01:00 HTTP "GET" "/swagger" responded 302 in 71.2008 ms
109 | 12/09/2022 01:18:49 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 266.2446 ms
110 | 12/09/2022 01:18:49 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 48.6887 ms
111 | 12/09/2022 01:18:49 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 48.9039 ms
112 | 12/09/2022 01:18:49 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 48.9418 ms
113 | 12/09/2022 01:18:50 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 795.1755 ms
114 | 12/09/2022 01:19:32 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 39.8699 ms
115 | 12/09/2022 01:19:33 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 643.7539 ms
116 | 12/09/2022 01:19:36 +01:00 HTTP "GET" "/api/soaps" responded 200 in 390.6769 ms
117 | 12/09/2022 01:19:49 +01:00 HTTP "OPTIONS" "/api/soaps/80291510-04b6-4674-ea0c-08dad97af143" responded 204 in 1.7393 ms
118 | 12/09/2022 01:19:49 +01:00 HTTP "DELETE" "/api/soaps/80291510-04b6-4674-ea0c-08dad97af143" responded 200 in 350.4012 ms
119 | 12/09/2022 01:19:51 +01:00 HTTP "OPTIONS" "/api/soaps/f274de95-617b-4853-ea0d-08dad97af143" responded 204 in 2.0896 ms
120 | 12/09/2022 01:19:51 +01:00 HTTP "DELETE" "/api/soaps/f274de95-617b-4853-ea0d-08dad97af143" responded 200 in 39.0143 ms
121 | 12/09/2022 01:19:52 +01:00 HTTP "OPTIONS" "/api/soaps/a0420b16-6dc9-4814-ea0e-08dad97af143" responded 204 in 1.6985 ms
122 | 12/09/2022 01:19:52 +01:00 HTTP "DELETE" "/api/soaps/a0420b16-6dc9-4814-ea0e-08dad97af143" responded 200 in 90.9983 ms
123 | 12/09/2022 01:22:12 +01:00 Application is starting
124 | 12/09/2022 01:22:16 +01:00 HTTP "GET" "/swagger" responded 302 in 79.7484 ms
125 | 12/09/2022 01:22:16 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 260.6743 ms
126 | 12/09/2022 01:22:16 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 53.5819 ms
127 | 12/09/2022 01:22:16 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 53.5405 ms
128 | 12/09/2022 01:22:16 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 55.3886 ms
129 | 12/09/2022 01:22:17 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 881.6002 ms
130 | 12/09/2022 01:22:20 +01:00 HTTP "GET" "/api/soaps" responded 200 in 300.1814 ms
131 | 12/09/2022 01:49:15 +01:00 Application is starting
132 | 12/09/2022 01:49:19 +01:00 HTTP "GET" "/swagger" responded 302 in 74.0062 ms
133 | 12/09/2022 01:49:19 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 241.9837 ms
134 | 12/09/2022 01:49:20 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 53.0287 ms
135 | 12/09/2022 01:49:20 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 52.7231 ms
136 | 12/09/2022 01:49:20 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 53.0381 ms
137 | 12/09/2022 01:49:21 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 709.1719 ms
138 | 12/09/2022 01:50:07 +01:00 HTTP "GET" "/api/balms" responded 200 in 431.9337 ms
139 | 12/09/2022 01:50:09 +01:00 HTTP "GET" "/api/soaps" responded 200 in 289.4988 ms
140 | 12/09/2022 01:50:15 +01:00 HTTP "OPTIONS" "/api/account/signin" responded 204 in 4.9255 ms
141 | 12/09/2022 01:50:15 +01:00 HTTP "POST" "/api/account/signin" responded 200 in 584.5419 ms
142 | 12/09/2022 01:50:16 +01:00 HTTP "GET" "/api/soaps" responded 200 in 13.0885 ms
143 | 12/09/2022 01:50:19 +01:00 HTTP "OPTIONS" "/api/soaps/2fa0d1bd-0932-404f-7ec3-08dad97b6c9b" responded 204 in 0.9698 ms
144 | 12/09/2022 01:50:19 +01:00 HTTP "DELETE" "/api/soaps/2fa0d1bd-0932-404f-7ec3-08dad97b6c9b" responded 200 in 245.9339 ms
145 | 12/09/2022 01:50:21 +01:00 HTTP "OPTIONS" "/api/soaps/2e0a9dc1-6719-4c71-7ec2-08dad97b6c9b" responded 204 in 0.8591 ms
146 | 12/09/2022 01:50:21 +01:00 HTTP "DELETE" "/api/soaps/2e0a9dc1-6719-4c71-7ec2-08dad97b6c9b" responded 200 in 85.0612 ms
147 | 12/09/2022 01:50:22 +01:00 HTTP "OPTIONS" "/api/soaps/c18310f3-0d4a-4a9e-7ec1-08dad97b6c9b" responded 204 in 0.9214 ms
148 | 12/09/2022 01:50:22 +01:00 HTTP "DELETE" "/api/soaps/c18310f3-0d4a-4a9e-7ec1-08dad97b6c9b" responded 200 in 101.9597 ms
149 | 12/09/2022 01:50:23 +01:00 HTTP "GET" "/api/balms" responded 200 in 7.7168 ms
150 | 12/09/2022 01:50:26 +01:00 HTTP "GET" "/api/oils" responded 200 in 121.7575 ms
151 | 12/09/2022 01:50:36 +01:00 Application is starting
152 | 12/09/2022 01:50:40 +01:00 HTTP "GET" "/swagger" responded 302 in 74.4629 ms
153 | 12/09/2022 01:50:40 +01:00 HTTP "GET" "/swagger/index.html" responded 200 in 233.1627 ms
154 | 12/09/2022 01:50:40 +01:00 HTTP "GET" "/swagger/swagger-ui-standalone-preset.js" responded 200 in 51.0331 ms
155 | 12/09/2022 01:50:40 +01:00 HTTP "GET" "/swagger/swagger-ui.css" responded 200 in 51.0382 ms
156 | 12/09/2022 01:50:40 +01:00 HTTP "GET" "/swagger/swagger-ui-bundle.js" responded 200 in 52.6252 ms
157 | 12/09/2022 01:50:41 +01:00 HTTP "GET" "/swagger/v1/swagger.json" responded 200 in 914.5669 ms
158 | 12/09/2022 01:50:46 +01:00 HTTP "GET" "/api/soaps" responded 200 in 334.3577 ms
159 | 12/09/2022 01:50:52 +01:00 HTTP "GET" "/api/balms" responded 200 in 232.1565 ms
160 | 12/09/2022 01:50:52 +01:00 HTTP "GET" "/api/oils" responded 200 in 162.8215 ms
161 | 12/09/2022 01:50:58 +01:00 HTTP "GET" "/api/oils" responded 200 in 7.4290 ms
162 | 12/09/2022 01:51:00 +01:00 HTTP "GET" "/api/balms" responded 200 in 12.6530 ms
163 | 12/09/2022 01:51:05 +01:00 HTTP "GET" "/api/soaps" responded 200 in 36.4332 ms
164 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Migrations/20221129220853_init db.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Migrations
5 | {
6 | public partial class initdb : Migration
7 | {
8 | protected override void Up(MigrationBuilder migrationBuilder)
9 | {
10 | migrationBuilder.CreateTable(
11 | name: "AspNetRoles",
12 | columns: table => new
13 | {
14 | Id = table.Column(type: "nvarchar(450)", nullable: false),
15 | Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
16 | NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
17 | ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true)
18 | },
19 | constraints: table =>
20 | {
21 | table.PrimaryKey("PK_AspNetRoles", x => x.Id);
22 | });
23 |
24 | migrationBuilder.CreateTable(
25 | name: "AspNetUsers",
26 | columns: table => new
27 | {
28 | Id = table.Column(type: "nvarchar(450)", nullable: false),
29 | UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
30 | NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
31 | Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
32 | NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true),
33 | EmailConfirmed = table.Column(type: "bit", nullable: false),
34 | PasswordHash = table.Column(type: "nvarchar(max)", nullable: true),
35 | SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true),
36 | ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true),
37 | PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true),
38 | PhoneNumberConfirmed = table.Column(type: "bit", nullable: false),
39 | TwoFactorEnabled = table.Column(type: "bit", nullable: false),
40 | LockoutEnd = table.Column(type: "datetimeoffset", nullable: true),
41 | LockoutEnabled = table.Column(type: "bit", nullable: false),
42 | AccessFailedCount = table.Column(type: "int", nullable: false)
43 | },
44 | constraints: table =>
45 | {
46 | table.PrimaryKey("PK_AspNetUsers", x => x.Id);
47 | });
48 |
49 | migrationBuilder.CreateTable(
50 | name: "Balms",
51 | columns: table => new
52 | {
53 | Id = table.Column(type: "uniqueidentifier", nullable: false),
54 | Volume = table.Column(type: "int", nullable: false),
55 | CreatedBy = table.Column(type: "nvarchar(max)", nullable: true),
56 | Created = table.Column(type: "datetime2", nullable: false),
57 | LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true),
58 | LastModified = table.Column(type: "datetime2", nullable: false),
59 | Brand = table.Column(type: "nvarchar(max)", nullable: true),
60 | Description = table.Column(type: "nvarchar(max)", nullable: true),
61 | UnitPrice = table.Column(type: "int", nullable: false),
62 | UnitQuantity = table.Column(type: "int", nullable: false),
63 | URL = table.Column(type: "nvarchar(max)", nullable: true)
64 | },
65 | constraints: table =>
66 | {
67 | table.PrimaryKey("PK_Balms", x => x.Id);
68 | });
69 |
70 | migrationBuilder.CreateTable(
71 | name: "Oils",
72 | columns: table => new
73 | {
74 | Id = table.Column(type: "uniqueidentifier", nullable: false),
75 | Scent = table.Column(type: "nvarchar(max)", nullable: true),
76 | LiquidVolume = table.Column(type: "int", nullable: false),
77 | CreatedBy = table.Column(type: "nvarchar(max)", nullable: true),
78 | Created = table.Column(type: "datetime2", nullable: false),
79 | LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true),
80 | LastModified = table.Column(type: "datetime2", nullable: false),
81 | Brand = table.Column(type: "nvarchar(max)", nullable: true),
82 | Description = table.Column(type: "nvarchar(max)", nullable: true),
83 | UnitPrice = table.Column(type: "int", nullable: false),
84 | UnitQuantity = table.Column(type: "int", nullable: false),
85 | URL = table.Column(type: "nvarchar(max)", nullable: true)
86 | },
87 | constraints: table =>
88 | {
89 | table.PrimaryKey("PK_Oils", x => x.Id);
90 | });
91 |
92 | migrationBuilder.CreateTable(
93 | name: "Soaps",
94 | columns: table => new
95 | {
96 | Id = table.Column(type: "uniqueidentifier", nullable: false),
97 | Edition = table.Column(type: "nvarchar(max)", nullable: true),
98 | CreatedBy = table.Column(type: "nvarchar(max)", nullable: true),
99 | Created = table.Column(type: "datetime2", nullable: false),
100 | LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true),
101 | LastModified = table.Column(type: "datetime2", nullable: false),
102 | Brand = table.Column(type: "nvarchar(max)", nullable: true),
103 | Description = table.Column(type: "nvarchar(max)", nullable: true),
104 | UnitPrice = table.Column(type: "int", nullable: false),
105 | UnitQuantity = table.Column(type: "int", nullable: false),
106 | URL = table.Column(type: "nvarchar(max)", nullable: true)
107 | },
108 | constraints: table =>
109 | {
110 | table.PrimaryKey("PK_Soaps", x => x.Id);
111 | });
112 |
113 | migrationBuilder.CreateTable(
114 | name: "AspNetRoleClaims",
115 | columns: table => new
116 | {
117 | Id = table.Column(type: "int", nullable: false)
118 | .Annotation("SqlServer:Identity", "1, 1"),
119 | RoleId = table.Column(type: "nvarchar(450)", nullable: false),
120 | ClaimType = table.Column(type: "nvarchar(max)", nullable: true),
121 | ClaimValue = table.Column(type: "nvarchar(max)", nullable: true)
122 | },
123 | constraints: table =>
124 | {
125 | table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
126 | table.ForeignKey(
127 | name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
128 | column: x => x.RoleId,
129 | principalTable: "AspNetRoles",
130 | principalColumn: "Id",
131 | onDelete: ReferentialAction.Cascade);
132 | });
133 |
134 | migrationBuilder.CreateTable(
135 | name: "AspNetUserClaims",
136 | columns: table => new
137 | {
138 | Id = table.Column(type: "int", nullable: false)
139 | .Annotation("SqlServer:Identity", "1, 1"),
140 | UserId = table.Column(type: "nvarchar(450)", nullable: false),
141 | ClaimType = table.Column(type: "nvarchar(max)", nullable: true),
142 | ClaimValue = table.Column(type: "nvarchar(max)", nullable: true)
143 | },
144 | constraints: table =>
145 | {
146 | table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
147 | table.ForeignKey(
148 | name: "FK_AspNetUserClaims_AspNetUsers_UserId",
149 | column: x => x.UserId,
150 | principalTable: "AspNetUsers",
151 | principalColumn: "Id",
152 | onDelete: ReferentialAction.Cascade);
153 | });
154 |
155 | migrationBuilder.CreateTable(
156 | name: "AspNetUserLogins",
157 | columns: table => new
158 | {
159 | LoginProvider = table.Column(type: "nvarchar(450)", nullable: false),
160 | ProviderKey = table.Column(type: "nvarchar(450)", nullable: false),
161 | ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true),
162 | UserId = table.Column(type: "nvarchar(450)", nullable: false)
163 | },
164 | constraints: table =>
165 | {
166 | table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
167 | table.ForeignKey(
168 | name: "FK_AspNetUserLogins_AspNetUsers_UserId",
169 | column: x => x.UserId,
170 | principalTable: "AspNetUsers",
171 | principalColumn: "Id",
172 | onDelete: ReferentialAction.Cascade);
173 | });
174 |
175 | migrationBuilder.CreateTable(
176 | name: "AspNetUserRoles",
177 | columns: table => new
178 | {
179 | UserId = table.Column(type: "nvarchar(450)", nullable: false),
180 | RoleId = table.Column(type: "nvarchar(450)", nullable: false)
181 | },
182 | constraints: table =>
183 | {
184 | table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
185 | table.ForeignKey(
186 | name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
187 | column: x => x.RoleId,
188 | principalTable: "AspNetRoles",
189 | principalColumn: "Id",
190 | onDelete: ReferentialAction.Cascade);
191 | table.ForeignKey(
192 | name: "FK_AspNetUserRoles_AspNetUsers_UserId",
193 | column: x => x.UserId,
194 | principalTable: "AspNetUsers",
195 | principalColumn: "Id",
196 | onDelete: ReferentialAction.Cascade);
197 | });
198 |
199 | migrationBuilder.CreateTable(
200 | name: "AspNetUserTokens",
201 | columns: table => new
202 | {
203 | UserId = table.Column(type: "nvarchar(450)", nullable: false),
204 | LoginProvider = table.Column(type: "nvarchar(450)", nullable: false),
205 | Name = table.Column(type: "nvarchar(450)", nullable: false),
206 | Value = table.Column(type: "nvarchar(max)", nullable: true)
207 | },
208 | constraints: table =>
209 | {
210 | table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
211 | table.ForeignKey(
212 | name: "FK_AspNetUserTokens_AspNetUsers_UserId",
213 | column: x => x.UserId,
214 | principalTable: "AspNetUsers",
215 | principalColumn: "Id",
216 | onDelete: ReferentialAction.Cascade);
217 | });
218 |
219 | migrationBuilder.CreateIndex(
220 | name: "IX_AspNetRoleClaims_RoleId",
221 | table: "AspNetRoleClaims",
222 | column: "RoleId");
223 |
224 | migrationBuilder.CreateIndex(
225 | name: "RoleNameIndex",
226 | table: "AspNetRoles",
227 | column: "NormalizedName",
228 | unique: true,
229 | filter: "[NormalizedName] IS NOT NULL");
230 |
231 | migrationBuilder.CreateIndex(
232 | name: "IX_AspNetUserClaims_UserId",
233 | table: "AspNetUserClaims",
234 | column: "UserId");
235 |
236 | migrationBuilder.CreateIndex(
237 | name: "IX_AspNetUserLogins_UserId",
238 | table: "AspNetUserLogins",
239 | column: "UserId");
240 |
241 | migrationBuilder.CreateIndex(
242 | name: "IX_AspNetUserRoles_RoleId",
243 | table: "AspNetUserRoles",
244 | column: "RoleId");
245 |
246 | migrationBuilder.CreateIndex(
247 | name: "EmailIndex",
248 | table: "AspNetUsers",
249 | column: "NormalizedEmail");
250 |
251 | migrationBuilder.CreateIndex(
252 | name: "UserNameIndex",
253 | table: "AspNetUsers",
254 | column: "NormalizedUserName",
255 | unique: true,
256 | filter: "[NormalizedUserName] IS NOT NULL");
257 | }
258 |
259 | protected override void Down(MigrationBuilder migrationBuilder)
260 | {
261 | migrationBuilder.DropTable(
262 | name: "AspNetRoleClaims");
263 |
264 | migrationBuilder.DropTable(
265 | name: "AspNetUserClaims");
266 |
267 | migrationBuilder.DropTable(
268 | name: "AspNetUserLogins");
269 |
270 | migrationBuilder.DropTable(
271 | name: "AspNetUserRoles");
272 |
273 | migrationBuilder.DropTable(
274 | name: "AspNetUserTokens");
275 |
276 | migrationBuilder.DropTable(
277 | name: "Balms");
278 |
279 | migrationBuilder.DropTable(
280 | name: "Oils");
281 |
282 | migrationBuilder.DropTable(
283 | name: "Soaps");
284 |
285 | migrationBuilder.DropTable(
286 | name: "AspNetRoles");
287 |
288 | migrationBuilder.DropTable(
289 | name: "AspNetUsers");
290 | }
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/CleanArchitectureWebAPI.Infrastructure.Data/Migrations/LibraryDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using CleanArchitectureWebAPI.Infrastructure.Data.Context;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | namespace CleanArchitectureWebAPI.Infrastructure.Data.Migrations
10 | {
11 | [DbContext(typeof(LibraryDbContext))]
12 | partial class LibraryDbContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .UseIdentityColumns()
19 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
20 | .HasAnnotation("ProductVersion", "5.0.0");
21 |
22 | modelBuilder.Entity("CleanArchitectureWebAPI.Domian.Models.Balm", b =>
23 | {
24 | b.Property("Id")
25 | .ValueGeneratedOnAdd()
26 | .HasColumnType("uniqueidentifier");
27 |
28 | b.Property("Brand")
29 | .HasColumnType("nvarchar(max)");
30 |
31 | b.Property("Created")
32 | .HasColumnType("datetime2");
33 |
34 | b.Property("CreatedBy")
35 | .HasColumnType("nvarchar(max)");
36 |
37 | b.Property("Description")
38 | .HasColumnType("nvarchar(max)");
39 |
40 | b.Property("LastModified")
41 | .HasColumnType("datetime2");
42 |
43 | b.Property("LastModifiedBy")
44 | .HasColumnType("nvarchar(max)");
45 |
46 | b.Property("URL")
47 | .HasColumnType("nvarchar(max)");
48 |
49 | b.Property("UnitPrice")
50 | .HasColumnType("int");
51 |
52 | b.Property("UnitQuantity")
53 | .HasColumnType("int");
54 |
55 | b.Property("Volume")
56 | .HasColumnType("int");
57 |
58 | b.HasKey("Id");
59 |
60 | b.ToTable("Balms");
61 | });
62 |
63 | modelBuilder.Entity("CleanArchitectureWebAPI.Domian.Models.Oil", b =>
64 | {
65 | b.Property("Id")
66 | .ValueGeneratedOnAdd()
67 | .HasColumnType("uniqueidentifier");
68 |
69 | b.Property("Brand")
70 | .HasColumnType("nvarchar(max)");
71 |
72 | b.Property("Created")
73 | .HasColumnType("datetime2");
74 |
75 | b.Property("CreatedBy")
76 | .HasColumnType("nvarchar(max)");
77 |
78 | b.Property("Description")
79 | .HasColumnType("nvarchar(max)");
80 |
81 | b.Property("LastModified")
82 | .HasColumnType("datetime2");
83 |
84 | b.Property("LastModifiedBy")
85 | .HasColumnType("nvarchar(max)");
86 |
87 | b.Property("LiquidVolume")
88 | .HasColumnType("int");
89 |
90 | b.Property("Scent")
91 | .HasColumnType("nvarchar(max)");
92 |
93 | b.Property("URL")
94 | .HasColumnType("nvarchar(max)");
95 |
96 | b.Property("UnitPrice")
97 | .HasColumnType("int");
98 |
99 | b.Property("UnitQuantity")
100 | .HasColumnType("int");
101 |
102 | b.HasKey("Id");
103 |
104 | b.ToTable("Oils");
105 | });
106 |
107 | modelBuilder.Entity("CleanArchitectureWebAPI.Domian.Models.Soap", b =>
108 | {
109 | b.Property("Id")
110 | .ValueGeneratedOnAdd()
111 | .HasColumnType("uniqueidentifier");
112 |
113 | b.Property("Brand")
114 | .HasColumnType("nvarchar(max)");
115 |
116 | b.Property("Created")
117 | .HasColumnType("datetime2");
118 |
119 | b.Property("CreatedBy")
120 | .HasColumnType("nvarchar(max)");
121 |
122 | b.Property("Description")
123 | .HasColumnType("nvarchar(max)");
124 |
125 | b.Property("Edition")
126 | .HasColumnType("nvarchar(max)");
127 |
128 | b.Property("LastModified")
129 | .HasColumnType("datetime2");
130 |
131 | b.Property("LastModifiedBy")
132 | .HasColumnType("nvarchar(max)");
133 |
134 | b.Property("URL")
135 | .HasColumnType("nvarchar(max)");
136 |
137 | b.Property("UnitPrice")
138 | .HasColumnType("int");
139 |
140 | b.Property("UnitQuantity")
141 | .HasColumnType("int");
142 |
143 | b.HasKey("Id");
144 |
145 | b.ToTable("Soaps");
146 | });
147 |
148 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
149 | {
150 | b.Property("Id")
151 | .HasColumnType("nvarchar(450)");
152 |
153 | b.Property("ConcurrencyStamp")
154 | .IsConcurrencyToken()
155 | .HasColumnType("nvarchar(max)");
156 |
157 | b.Property("Name")
158 | .HasMaxLength(256)
159 | .HasColumnType("nvarchar(256)");
160 |
161 | b.Property("NormalizedName")
162 | .HasMaxLength(256)
163 | .HasColumnType("nvarchar(256)");
164 |
165 | b.HasKey("Id");
166 |
167 | b.HasIndex("NormalizedName")
168 | .IsUnique()
169 | .HasDatabaseName("RoleNameIndex")
170 | .HasFilter("[NormalizedName] IS NOT NULL");
171 |
172 | b.ToTable("AspNetRoles");
173 | });
174 |
175 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
176 | {
177 | b.Property("Id")
178 | .ValueGeneratedOnAdd()
179 | .HasColumnType("int")
180 | .UseIdentityColumn();
181 |
182 | b.Property("ClaimType")
183 | .HasColumnType("nvarchar(max)");
184 |
185 | b.Property("ClaimValue")
186 | .HasColumnType("nvarchar(max)");
187 |
188 | b.Property("RoleId")
189 | .IsRequired()
190 | .HasColumnType("nvarchar(450)");
191 |
192 | b.HasKey("Id");
193 |
194 | b.HasIndex("RoleId");
195 |
196 | b.ToTable("AspNetRoleClaims");
197 | });
198 |
199 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
200 | {
201 | b.Property("Id")
202 | .HasColumnType("nvarchar(450)");
203 |
204 | b.Property("AccessFailedCount")
205 | .HasColumnType("int");
206 |
207 | b.Property("ConcurrencyStamp")
208 | .IsConcurrencyToken()
209 | .HasColumnType("nvarchar(max)");
210 |
211 | b.Property("Email")
212 | .HasMaxLength(256)
213 | .HasColumnType("nvarchar(256)");
214 |
215 | b.Property("EmailConfirmed")
216 | .HasColumnType("bit");
217 |
218 | b.Property("LockoutEnabled")
219 | .HasColumnType("bit");
220 |
221 | b.Property("LockoutEnd")
222 | .HasColumnType("datetimeoffset");
223 |
224 | b.Property("NormalizedEmail")
225 | .HasMaxLength(256)
226 | .HasColumnType("nvarchar(256)");
227 |
228 | b.Property("NormalizedUserName")
229 | .HasMaxLength(256)
230 | .HasColumnType("nvarchar(256)");
231 |
232 | b.Property("PasswordHash")
233 | .HasColumnType("nvarchar(max)");
234 |
235 | b.Property("PhoneNumber")
236 | .HasColumnType("nvarchar(max)");
237 |
238 | b.Property("PhoneNumberConfirmed")
239 | .HasColumnType("bit");
240 |
241 | b.Property("SecurityStamp")
242 | .HasColumnType("nvarchar(max)");
243 |
244 | b.Property("TwoFactorEnabled")
245 | .HasColumnType("bit");
246 |
247 | b.Property("UserName")
248 | .HasMaxLength(256)
249 | .HasColumnType("nvarchar(256)");
250 |
251 | b.HasKey("Id");
252 |
253 | b.HasIndex("NormalizedEmail")
254 | .HasDatabaseName("EmailIndex");
255 |
256 | b.HasIndex("NormalizedUserName")
257 | .IsUnique()
258 | .HasDatabaseName("UserNameIndex")
259 | .HasFilter("[NormalizedUserName] IS NOT NULL");
260 |
261 | b.ToTable("AspNetUsers");
262 | });
263 |
264 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
265 | {
266 | b.Property("Id")
267 | .ValueGeneratedOnAdd()
268 | .HasColumnType("int")
269 | .UseIdentityColumn();
270 |
271 | b.Property("ClaimType")
272 | .HasColumnType("nvarchar(max)");
273 |
274 | b.Property("ClaimValue")
275 | .HasColumnType("nvarchar(max)");
276 |
277 | b.Property("UserId")
278 | .IsRequired()
279 | .HasColumnType("nvarchar(450)");
280 |
281 | b.HasKey("Id");
282 |
283 | b.HasIndex("UserId");
284 |
285 | b.ToTable("AspNetUserClaims");
286 | });
287 |
288 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
289 | {
290 | b.Property("LoginProvider")
291 | .HasColumnType("nvarchar(450)");
292 |
293 | b.Property("ProviderKey")
294 | .HasColumnType("nvarchar(450)");
295 |
296 | b.Property("ProviderDisplayName")
297 | .HasColumnType("nvarchar(max)");
298 |
299 | b.Property("UserId")
300 | .IsRequired()
301 | .HasColumnType("nvarchar(450)");
302 |
303 | b.HasKey("LoginProvider", "ProviderKey");
304 |
305 | b.HasIndex("UserId");
306 |
307 | b.ToTable("AspNetUserLogins");
308 | });
309 |
310 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
311 | {
312 | b.Property("UserId")
313 | .HasColumnType("nvarchar(450)");
314 |
315 | b.Property("RoleId")
316 | .HasColumnType("nvarchar(450)");
317 |
318 | b.HasKey("UserId", "RoleId");
319 |
320 | b.HasIndex("RoleId");
321 |
322 | b.ToTable("AspNetUserRoles");
323 | });
324 |
325 | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
326 | {
327 | b.Property("UserId")
328 | .HasColumnType("nvarchar(450)");
329 |
330 | b.Property("LoginProvider")
331 | .HasColumnType("nvarchar(450)");
332 |
333 | b.Property("Name")
334 | .HasColumnType("nvarchar(450)");
335 |
336 | b.Property