diff --git a/BLL/BLL.csproj b/BLL/BLL.csproj index 08d25cd..a20379b 100644 --- a/BLL/BLL.csproj +++ b/BLL/BLL.csproj @@ -6,7 +6,12 @@ - + + + + + + diff --git a/BLL/BLLDependency.cs b/BLL/BLLDependency.cs index 167f3fb..294f4ee 100644 --- a/BLL/BLLDependency.cs +++ b/BLL/BLLDependency.cs @@ -1,4 +1,5 @@ -using BLL.Request; +using BLL.Helpers; +using BLL.Request; using BLL.Services; using FluentValidation; using Microsoft.Extensions.Configuration; @@ -21,6 +22,8 @@ public static void AllDependency(IServiceCollection services, IConfiguration con services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); AllFluentValidationDependency(services); } diff --git a/BLL/Helpers/CustomFileValidator.cs b/BLL/Helpers/CustomFileValidator.cs new file mode 100644 index 0000000..6fd7603 --- /dev/null +++ b/BLL/Helpers/CustomFileValidator.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using FluentValidation.Resources; +using FluentValidation.Validators; +using Microsoft.AspNetCore.Http; + +namespace BLL.Helpers +{ + public class CustomFileValidator + { + + } + + //public class CustomFileValidator : AsyncValidatorBase + //{ + // private readonly IFileValidate _fileValidate; + + // public CustomFileValidator(IFileValidate fileValidate) : base("{ErrorMessage}") + // { + // _fileValidate = fileValidate; + // } + + // protected override async Task IsValidAsync(PropertyValidatorContext context, + // CancellationToken cancellation) + // { + // var fileToValidate = context.PropertyValue as IFormFile; + + + // var (valid, errorMessage) = _fileValidate.ValidateFile(fileToValidate); + + // if (valid) return true; + // context.MessageFormatter.AppendArgument("ErrorMessage", errorMessage); + // return false; + + // } + //} +} diff --git a/BLL/Helpers/FileValidate.cs b/BLL/Helpers/FileValidate.cs new file mode 100644 index 0000000..f7cb29c --- /dev/null +++ b/BLL/Helpers/FileValidate.cs @@ -0,0 +1,74 @@ +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.AspNetCore.Http; + +namespace BLL.Helpers +{ + public interface IFileValidate + { + (bool Valid, string ErrorMessage) ValidateFile(IFormFile filetoValidate); + } + + public class FileValidate : IFileValidate + { + + public (bool Valid, string ErrorMessage) ValidateFile(IFormFile fileToValidate) + { + if (fileToValidate == null) + { + return (false, $"Provide Valid Image File"); + } + if (!CheckIfImageFile(fileToValidate)) + { + return (false, $"Provide Valid Image File"); + } + return (true, $"Sample error message"); + + } + + private bool CheckIfImageFile(IFormFile file) + { + byte[] fileBytes; + using (var ms = new MemoryStream()) + { + file.CopyTo(ms); + fileBytes = ms.ToArray(); + } + + return GetImageFormat(fileBytes) != ImageFormat.unknown; + } + + public ImageFormat GetImageFormat(byte[] bytes) + { + var bmp = Encoding.ASCII.GetBytes("BM"); // BMP + var gif = Encoding.ASCII.GetBytes("GIF"); // GIF + var png = new byte[] { 137, 80, 78, 71 }; // PNG + var tiff = new byte[] { 73, 73, 42 }; // TIFF + var tiff2 = new byte[] { 77, 77, 42 }; // TIFF + var jpeg = new byte[] { 255, 216, 255, 224 }; // jpeg + var jpeg2 = new byte[] { 255, 216, 255, 225 }; // jpeg canon + + + + if (png.SequenceEqual(bytes.Take(png.Length))) + return ImageFormat.png; + + if (jpeg.SequenceEqual(bytes.Take(jpeg.Length))) + return ImageFormat.jpeg; + + if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length))) + return ImageFormat.jpeg; + + return ImageFormat.unknown; + } + + public enum ImageFormat + { + + jpeg, + png, + unknown + } + } +} \ No newline at end of file diff --git a/BLL/Request/CustomerMarker.cs b/BLL/Request/CustomerMarker.cs new file mode 100644 index 0000000..c2aecf1 --- /dev/null +++ b/BLL/Request/CustomerMarker.cs @@ -0,0 +1,9 @@ +using System.Security.Claims; + +namespace BLL.Request +{ + public class RequestMaker + { + public ClaimsPrincipal Principal { get; set; } + } +} \ No newline at end of file diff --git a/BLL/Services/TestService.cs b/BLL/Services/TestService.cs index dac3f51..8e3a330 100644 --- a/BLL/Services/TestService.cs +++ b/BLL/Services/TestService.cs @@ -2,6 +2,7 @@ using DLL.DBContext; using DLL.Models; using DLL.Repositories; +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; @@ -16,18 +17,32 @@ public interface ITestService Task InsertData(); Task DummyData1(); Task DummyData2(); + Task AddNewRoles(); + Task AddNewUser(); + //Task CreateAndroidAndWebClient(); } public class TestService : ITestService { private readonly IUnitOfWork _unitOfWork; private readonly ApplicationDbContext _context; + private readonly RoleManager _roleManager; + private readonly UserManager _userManager; + //private readonly IOpenIddictApplicationManager _openIddictApplicationManager; - public TestService(IUnitOfWork unitOfWork, ApplicationDbContext context) + public TestService(IUnitOfWork unitOfWork, + ApplicationDbContext context, RoleManager roleManager, + UserManager userManager + //IOpenIddictApplicationManager openIddictApplicationManager + ) { _unitOfWork = unitOfWork; _context = context; + _roleManager = roleManager; + _userManager = userManager; + //_openIddictApplicationManager = openIddictApplicationManager; + //_openIddictApplicationManager = openIddictApplicationManager; } public async Task InsertData() @@ -68,7 +83,6 @@ public async Task DummyData1() public async Task DummyData2() { - // var courseDummy = new Faker() // .RuleFor(o => o.Name, f => f.Name.FirstName()) // .RuleFor(o => o.Code, f => f.Name.LastName()) @@ -99,5 +113,133 @@ public async Task DummyData2() count += 5; } } + + public async Task AddNewRoles() + { + var roleList = new List() + { + "admin", + "manager", + "supervisor" + }; + + foreach (var role in roleList) + { + var exits = await _roleManager.FindByNameAsync(role); + + if (exits == null) + { + await _roleManager.CreateAsync(new ApplicationRole() + { + Name = role + }); + } + } + } + + public async Task AddNewUser() + { + var userList = new List() + { + new ApplicationUser() + { + UserName = "tapos.aa@gmail.com", + Email = "tapos.aa@gmail.com", + FullName = "biswa nath ghosh tapos" + }, + new ApplicationUser() + { + UserName = "sanjib@gmail.com", + Email = "sanjib@gmail.com", + FullName = "sanjib dhar" + }, + new ApplicationUser() + { + UserName = "monir@gmail.com", + Email = "monir@gmail.com", + FullName = "monir hossain" + }, + }; + + foreach (var user in userList) + { + var userExits = await _userManager.FindByEmailAsync(user.Email); + + if (userExits == null) + { + var insertedData = await _userManager.CreateAsync(user, "abc123$..A!"); + + if (insertedData.Succeeded) + { + var myRole = ""; + if (user.Email == "tapos.aa@gmail.com") + { + myRole = "admin"; + } + else if (user.Email == "sanjib@gmail.com") + { + myRole = "manager"; + } + else if (user.Email == "monir@gmail.com") + { + myRole = "supervisor"; + } + + await _userManager.AddToRoleAsync(user, myRole); + } + } + } + } + + //public async Task CreateAndroidAndWebClient() + //{ + // var listOfClient = new List() + // { + // new OpenIddictApplicationDescriptor() + // { + // ClientId = "udemy_android_application", + // ClientSecret = "udemy123", + // DisplayName = "our android client", + // Permissions = + // { + // OpenIddictConstants.Permissions.Endpoints.Authorization, + // OpenIddictConstants.Permissions.Endpoints.Logout, + // OpenIddictConstants.Permissions.Endpoints.Token, + // OpenIddictConstants.Permissions.GrantTypes.Password, + // OpenIddictConstants.Permissions.GrantTypes.RefreshToken, + // OpenIddictConstants.Permissions.Scopes.Email, + // OpenIddictConstants.Permissions.Scopes.Profile, + // OpenIddictConstants.Permissions.Scopes.Roles, + // } + // }, + // new OpenIddictApplicationDescriptor() + // { + // ClientId = "udemy_web_application", + // ClientSecret = "udemy456", + // DisplayName = "our web application client", + // Permissions = + // { + // OpenIddictConstants.Permissions.Endpoints.Authorization, + // OpenIddictConstants.Permissions.Endpoints.Logout, + // OpenIddictConstants.Permissions.Endpoints.Token, + // OpenIddictConstants.Permissions.GrantTypes.Password, + // OpenIddictConstants.Permissions.GrantTypes.RefreshToken, + // OpenIddictConstants.Permissions.Scopes.Email, + // OpenIddictConstants.Permissions.Scopes.Profile, + // OpenIddictConstants.Permissions.Scopes.Roles, + // } + // } + // }; + + // foreach (var application in listOfClient) + // { + // var applicationExists = await _openIddictApplicationManager.FindByClientIdAsync(application.ClientId); + + // if (applicationExists == null) + // { + // await _openIddictApplicationManager.CreateAsync(application); + // } + // } + //} } } diff --git a/BLL/Services/TransactionService.cs b/BLL/Services/TransactionService.cs new file mode 100644 index 0000000..925bdbb --- /dev/null +++ b/BLL/Services/TransactionService.cs @@ -0,0 +1,56 @@ +using DLL.Models; +using DLL.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BLL.Services +{ + public interface ITransactionService + { + Task FinancialTransaction(); + } + + public class TransactionService : ITransactionService + { + private readonly IUnitOfWork _unitOfWork; + + public TransactionService(IUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public async Task FinancialTransaction() + { + //var rand = new Random(); + //var amount = rand.Next(1000); + //var transaction = new TransactionHistory() + //{ + // Amount = amount + //}; + //var CustomerBalance = await _unitOfWork.CustomerBalanceRepository + // .FindAsync(x => x.Email == "ghabiassaad@gmail.com"); + //CustomerBalance.Balance += amount; + //await _unitOfWork.TransactionHistoryRepository.CreateAsync(transaction); + //_unitOfWork.CustomerBalanceRepository.Update(CustomerBalance); + //await _unitOfWork.SaveAsync(); + + + var rand = new Random(); + var amount = rand.Next(1000); + var transaction = new TransactionHistory() + { + Amount = amount + }; + await _unitOfWork.TransactionHistoryRepository.CreateAsync(transaction); + if (await _unitOfWork.SaveAsync()) + { + await _unitOfWork.CustomerBalanceRepository.MustUpdateBalanceAsync("ghabiassaad@gmail.com", amount); + }; + + + } + } +} diff --git a/DLL/DBContext/ApplicationDbContext.cs b/DLL/DBContext/ApplicationDbContext.cs index 941e416..1fc4615 100644 --- a/DLL/DBContext/ApplicationDbContext.cs +++ b/DLL/DBContext/ApplicationDbContext.cs @@ -1,5 +1,7 @@ using DLL.Models; using DLL.Models.Interfaces; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using System; @@ -13,21 +15,22 @@ namespace DLL.DBContext { - public class ApplicationDbContext : DbContext + public class ApplicationDbContext : IdentityDbContext< + ApplicationUser, ApplicationRole, int, + IdentityUserClaim, ApplicationUserRole, IdentityUserLogin, + IdentityRoleClaim, IdentityUserToken> + + { private const string IsDeletedProperty = "IsDeleted"; + private static readonly MethodInfo _propertyMethod = typeof(EF) .GetMethod(nameof(EF.Property), BindingFlags.Static | BindingFlags.Public)?.MakeGenericMethod(typeof(bool)); - public ApplicationDbContext(DbContextOptions options) : base(options) + public ApplicationDbContext(DbContextOptions options) : base(options) { } - public DbSet Departments { get; set; } - public DbSet Students { get; set; } - public DbSet Courses { get; set; } - public DbSet CourseStudents { get; set; } - private static LambdaExpression GetIsDeletedRestriction(Type type) { var parm = Expression.Parameter(type, "it"); @@ -36,8 +39,11 @@ private static LambdaExpression GetIsDeletedRestriction(Type type) var lambda = Expression.Lambda(condition, parm); return lambda; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity() + .Property(p => p.RowVersion).IsConcurrencyToken(); foreach (var entity in modelBuilder.Model.GetEntityTypes()) { if (typeof(ISoftDeletable).IsAssignableFrom(entity.ClrType) == true) @@ -47,6 +53,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } } + base.OnModelCreating(modelBuilder); modelBuilder.Entity() .HasKey(bc => new { bc.CourseId, bc.StudentId }); modelBuilder.Entity() @@ -57,8 +64,59 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasOne(bc => bc.Student) .WithMany(c => c.CourseStudents) .HasForeignKey(bc => bc.StudentId); - base.OnModelCreating(modelBuilder); - } + + + modelBuilder.Entity(b => + { + // Each User can have many UserClaims + b.HasMany(e => e.Claims) + .WithOne() + .HasForeignKey(uc => uc.UserId) + .IsRequired(); + + // Each User can have many UserLogins + b.HasMany(e => e.Logins) + .WithOne() + .HasForeignKey(ul => ul.UserId) + .IsRequired(); + + // Each User can have many UserTokens + b.HasMany(e => e.Tokens) + .WithOne() + .HasForeignKey(ut => ut.UserId) + .IsRequired(); + + // Each User can have many entries in the UserRole join table + b.HasMany(e => e.UserRoles) + .WithOne(e => e.User) + .HasForeignKey(ur => ur.UserId) + .IsRequired(); + }); + + modelBuilder.Entity(b => + { + // Each Role can have many entries in the UserRole join table + b.HasMany(e => e.UserRoles) + .WithOne(e => e.Role) + .HasForeignKey(ur => ur.RoleId) + .IsRequired(); + }); + + modelBuilder.Entity(c => + { + c.Property(d => d.Name).HasMaxLength(100); + c.Property(d => d.Code).HasMaxLength(50); + c.Property(d => d.CreatedBy).HasMaxLength(100); + c.Property(d => d.LastUpdatedBy).HasMaxLength(100); + }); + } + + public override int SaveChanges(bool acceptAllChangesOnSuccess) + { + OnBeforeSavingData(); + return base.SaveChanges(acceptAllChangesOnSuccess); + } + private void OnBeforeSavingData() { ChangeTracker.DetectChanges(); @@ -84,18 +142,26 @@ private void OnBeforeSavingData() break; } } - } } - public override int SaveChanges(bool acceptAllChangesOnSuccess) - { - OnBeforeSavingData(); - return base.SaveChanges(acceptAllChangesOnSuccess); - } - public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()) + + public override Task SaveChangesAsync(bool acceptAllChangesOnSuccess, + CancellationToken cancellationToken = new CancellationToken()) { OnBeforeSavingData(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } + + public DbSet Departments { get; set; } + + public DbSet Students { get; set; } + public DbSet Courses { get; set; } + public DbSet CourseStudents { get; set; } + + // for concurrecy Example + public DbSet CustomerBalances { get; set; } + + public DbSet TransactionHistories { get; set; } + // end concurrency example } } diff --git a/DLL/DLL.csproj b/DLL/DLL.csproj index c79d2e5..25a3836 100644 --- a/DLL/DLL.csproj +++ b/DLL/DLL.csproj @@ -5,6 +5,8 @@ + + all diff --git a/DLL/Migrations/20210508234109_identity-migration.Designer.cs b/DLL/Migrations/20210508234109_identity-migration.Designer.cs new file mode 100644 index 0000000..070ec32 --- /dev/null +++ b/DLL/Migrations/20210508234109_identity-migration.Designer.cs @@ -0,0 +1,536 @@ +// +using System; +using DLL.DBContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace DLL.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20210508234109_identity-migration")] + partial class identitymigration + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.5") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("DLL.Models.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FullName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUserRole", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("DLL.Models.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Code") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Credit") + .HasColumnType("decimal(18,2)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("DLL.Models.CourseStudent", b => + { + b.Property("CourseId") + .HasColumnType("int"); + + b.Property("StudentId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("CourseId", "StudentId"); + + b.HasIndex("StudentId"); + + b.ToTable("CourseStudents"); + }); + + modelBuilder.Entity("DLL.Models.CustomerBalance", b => + { + b.Property("CustomerBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Balance") + .HasColumnType("decimal(18,2)"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("CustomerBalanceId"); + + b.ToTable("CustomerBalances"); + }); + + modelBuilder.Entity("DLL.Models.Department", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Departments"); + }); + + modelBuilder.Entity("DLL.Models.Student", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DepartmentId") + .HasColumnType("int"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DepartmentId"); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("DLL.Models.TransactionHistory", b => + { + b.Property("TransactionHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.HasKey("TransactionHistoryId"); + + b.ToTable("TransactionHistories"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUserRole", b => + { + b.HasOne("DLL.Models.ApplicationRole", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DLL.Models.ApplicationUser", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("DLL.Models.CourseStudent", b => + { + b.HasOne("DLL.Models.Course", "Course") + .WithMany("CourseStudents") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DLL.Models.Student", "Student") + .WithMany("CourseStudents") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Course"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("DLL.Models.Student", b => + { + b.HasOne("DLL.Models.Department", "Department") + .WithMany("Students") + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Department"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("DLL.Models.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("DLL.Models.ApplicationUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("DLL.Models.ApplicationUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("DLL.Models.ApplicationUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("DLL.Models.ApplicationRole", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("Tokens"); + + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("DLL.Models.Course", b => + { + b.Navigation("CourseStudents"); + }); + + modelBuilder.Entity("DLL.Models.Department", b => + { + b.Navigation("Students"); + }); + + modelBuilder.Entity("DLL.Models.Student", b => + { + b.Navigation("CourseStudents"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DLL/Migrations/20210508234109_identity-migration.cs b/DLL/Migrations/20210508234109_identity-migration.cs new file mode 100644 index 0000000..d5e654b --- /dev/null +++ b/DLL/Migrations/20210508234109_identity-migration.cs @@ -0,0 +1,353 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace DLL.Migrations +{ + public partial class identitymigration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DepartmentId", + table: "Students", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + FullName = table.Column(type: "nvarchar(max)", nullable: true), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + LastUpdatedAt = table.Column(type: "datetimeoffset", nullable: false), + LastUpdatedBy = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Courses", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Code = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Credit = table.Column(type: "decimal(18,2)", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + LastUpdatedAt = table.Column(type: "datetimeoffset", nullable: false), + LastUpdatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Courses", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "CustomerBalances", + columns: table => new + { + CustomerBalanceId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Email = table.Column(type: "nvarchar(max)", nullable: true), + Balance = table.Column(type: "decimal(18,2)", nullable: false), + RowVersion = table.Column(type: "rowversion", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomerBalances", x => x.CustomerBalanceId); + }); + + migrationBuilder.CreateTable( + name: "TransactionHistories", + columns: table => new + { + TransactionHistoryId = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Amount = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TransactionHistories", x => x.TransactionHistoryId); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "int", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "int", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "int", nullable: false), + RoleId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "int", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "CourseStudents", + columns: table => new + { + CourseId = table.Column(type: "int", nullable: false), + StudentId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + LastUpdatedAt = table.Column(type: "datetimeoffset", nullable: false), + LastUpdatedBy = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CourseStudents", x => new { x.CourseId, x.StudentId }); + table.ForeignKey( + name: "FK_CourseStudents_Courses_CourseId", + column: x => x.CourseId, + principalTable: "Courses", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_CourseStudents_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Students_DepartmentId", + table: "Students", + column: "DepartmentId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_CourseStudents_StudentId", + table: "CourseStudents", + column: "StudentId"); + + migrationBuilder.AddForeignKey( + name: "FK_Students_Departments_DepartmentId", + table: "Students", + column: "DepartmentId", + principalTable: "Departments", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Students_Departments_DepartmentId", + table: "Students"); + + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "CourseStudents"); + + migrationBuilder.DropTable( + name: "CustomerBalances"); + + migrationBuilder.DropTable( + name: "TransactionHistories"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "Courses"); + + migrationBuilder.DropIndex( + name: "IX_Students_DepartmentId", + table: "Students"); + + migrationBuilder.DropColumn( + name: "DepartmentId", + table: "Students"); + } + } +} diff --git a/DLL/Migrations/ApplicationDbContextModelSnapshot.cs b/DLL/Migrations/ApplicationDbContextModelSnapshot.cs index bec5440..29c9c20 100644 --- a/DLL/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/DLL/Migrations/ApplicationDbContextModelSnapshot.cs @@ -19,6 +19,228 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "5.0.5") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + modelBuilder.Entity("DLL.Models.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FullName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUserRole", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("DLL.Models.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Code") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Credit") + .HasColumnType("decimal(18,2)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("DLL.Models.CourseStudent", b => + { + b.Property("CourseId") + .HasColumnType("int"); + + b.Property("StudentId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("LastUpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("CourseId", "StudentId"); + + b.HasIndex("StudentId"); + + b.ToTable("CourseStudents"); + }); + + modelBuilder.Entity("DLL.Models.CustomerBalance", b => + { + b.Property("CustomerBalanceId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Balance") + .HasColumnType("decimal(18,2)"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("CustomerBalanceId"); + + b.ToTable("CustomerBalances"); + }); + modelBuilder.Entity("DLL.Models.Department", b => { b.Property("Id") @@ -65,6 +287,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .HasColumnType("nvarchar(max)"); + b.Property("DepartmentId") + .HasColumnType("int"); + b.Property("Email") .HasColumnType("nvarchar(max)"); @@ -82,8 +307,227 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("DepartmentId"); + b.ToTable("Students"); }); + + modelBuilder.Entity("DLL.Models.TransactionHistory", b => + { + b.Property("TransactionHistoryId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.HasKey("TransactionHistoryId"); + + b.ToTable("TransactionHistories"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("int"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUserRole", b => + { + b.HasOne("DLL.Models.ApplicationRole", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DLL.Models.ApplicationUser", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("DLL.Models.CourseStudent", b => + { + b.HasOne("DLL.Models.Course", "Course") + .WithMany("CourseStudents") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DLL.Models.Student", "Student") + .WithMany("CourseStudents") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Course"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("DLL.Models.Student", b => + { + b.HasOne("DLL.Models.Department", "Department") + .WithMany("Students") + .HasForeignKey("DepartmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Department"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("DLL.Models.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("DLL.Models.ApplicationUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("DLL.Models.ApplicationUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("DLL.Models.ApplicationUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("DLL.Models.ApplicationRole", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("DLL.Models.ApplicationUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("Tokens"); + + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("DLL.Models.Course", b => + { + b.Navigation("CourseStudents"); + }); + + modelBuilder.Entity("DLL.Models.Department", b => + { + b.Navigation("Students"); + }); + + modelBuilder.Entity("DLL.Models.Student", b => + { + b.Navigation("CourseStudents"); + }); #pragma warning restore 612, 618 } } diff --git a/DLL/Models/ApplicationRole.cs b/DLL/Models/ApplicationRole.cs new file mode 100644 index 0000000..08ba83d --- /dev/null +++ b/DLL/Models/ApplicationRole.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Identity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Models +{ + public class ApplicationRole : IdentityRole + { + public virtual ICollection UserRoles { get; set; } + } +} diff --git a/DLL/Models/ApplicationUser.cs b/DLL/Models/ApplicationUser.cs new file mode 100644 index 0000000..b9562d2 --- /dev/null +++ b/DLL/Models/ApplicationUser.cs @@ -0,0 +1,24 @@ +using DLL.Models.Interfaces; +using Microsoft.AspNetCore.Identity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Models +{ + public class ApplicationUser : IdentityUser, ITrackable, ISoftDeletable + { + public string FullName { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string CreatedBy { get; set; } + public DateTimeOffset LastUpdatedAt { get; set; } + public string LastUpdatedBy { get; set; } + + public virtual ICollection> Claims { get; set; } + public virtual ICollection> Logins { get; set; } + public virtual ICollection> Tokens { get; set; } + public virtual ICollection UserRoles { get; set; } + } +} diff --git a/DLL/Models/ApplicationUserRole.cs b/DLL/Models/ApplicationUserRole.cs new file mode 100644 index 0000000..2047bee --- /dev/null +++ b/DLL/Models/ApplicationUserRole.cs @@ -0,0 +1,16 @@ +using DLL.Models.Interfaces; +using Microsoft.AspNetCore.Identity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Models +{ + public class ApplicationUserRole : IdentityUserRole + { + public virtual ApplicationUser User { get; set; } + public virtual ApplicationRole Role { get; set; } + } +} diff --git a/DLL/Models/CustomerBalance.cs b/DLL/Models/CustomerBalance.cs new file mode 100644 index 0000000..c9e3565 --- /dev/null +++ b/DLL/Models/CustomerBalance.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Models +{ + public class CustomerBalance + { + public long CustomerBalanceId { get; set; } + public string Email { get; set; } + public Decimal Balance { get; set; } + + [Timestamp] + public byte[] RowVersion { get; set; } + } +} diff --git a/DLL/Models/TransactionHistory.cs b/DLL/Models/TransactionHistory.cs new file mode 100644 index 0000000..0035f1d --- /dev/null +++ b/DLL/Models/TransactionHistory.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Models +{ + public class TransactionHistory + { + public long TransactionHistoryId { get; set; } + public decimal Amount { get; set; } + } +} diff --git a/DLL/Repositories/CustomerBalanceRepository.cs b/DLL/Repositories/CustomerBalanceRepository.cs new file mode 100644 index 0000000..3332d29 --- /dev/null +++ b/DLL/Repositories/CustomerBalanceRepository.cs @@ -0,0 +1,58 @@ +using DLL.DBContext; +using DLL.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Repositories +{ + public interface ICustomerBalanceRepository : IBaseRepository + { + Task MustUpdateBalanceAsync(string email, decimal amount); + } + + public class CustomerBalanceRepository : BaseRepository, ICustomerBalanceRepository + { + private readonly ApplicationDbContext _context; + + public CustomerBalanceRepository(ApplicationDbContext context) : base(context) + { + _context = context; + } + + public async Task MustUpdateBalanceAsync(string email, decimal amount) + { + var customerBalance = + await _context.CustomerBalances.FirstOrDefaultAsync(x => x.Email == "ghabiassaad@gmail.com"); + customerBalance.Balance += amount; + bool isUpdated = false; + do + { + try + { + + if (await _context.SaveChangesAsync() > 0) + { + isUpdated = true; + }; + } + catch (DbUpdateConcurrencyException ex) + { + foreach (var entry in ex.Entries) + { + if (!(entry.Entity is CustomerBalance)) continue; + var databaseEntry = entry.GetDatabaseValues(); + var databaseValues = (CustomerBalance)databaseEntry.ToObject(); + databaseValues.Balance += amount; + + entry.OriginalValues.SetValues(databaseEntry); + entry.CurrentValues.SetValues(databaseValues); + } + } + } while (!isUpdated); + } + } +} diff --git a/DLL/Repositories/TransactionHistoryRepository.cs b/DLL/Repositories/TransactionHistoryRepository.cs new file mode 100644 index 0000000..ba79ba5 --- /dev/null +++ b/DLL/Repositories/TransactionHistoryRepository.cs @@ -0,0 +1,22 @@ +using DLL.DBContext; +using DLL.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DLL.Repositories +{ + public interface ITransactionHistoryRepository : IBaseRepository + { + + } + + public class TransactionHistoryRepository : BaseRepository, ITransactionHistoryRepository + { + public TransactionHistoryRepository(ApplicationDbContext context) : base(context) + { + } + } +} diff --git a/DLL/Repositories/UnitOfWork.cs b/DLL/Repositories/UnitOfWork.cs index 1c6465d..8315782 100644 --- a/DLL/Repositories/UnitOfWork.cs +++ b/DLL/Repositories/UnitOfWork.cs @@ -13,6 +13,8 @@ public interface IUnitOfWork IStudentRepository StudentRepository { get; } ICourseRepository CourseRepository { get; } ICourseStudentRepository CourseStudentRepository { get; } + ICustomerBalanceRepository CustomerBalanceRepository { get; } + ITransactionHistoryRepository TransactionHistoryRepository { get; } Task SaveAsync(); } @@ -30,6 +32,8 @@ public UnitOfWork(ApplicationDbContext context) private IStudentRepository _studentRepository; private ICourseRepository _courseRepository; private ICourseStudentRepository _courseStudentRepository; + private ICustomerBalanceRepository _customerBalanceRepository; + private ITransactionHistoryRepository _transactionHistoryRepository; public IDepartmentRepository DepartmentRepository => @@ -43,6 +47,12 @@ public UnitOfWork(ApplicationDbContext context) public ICourseStudentRepository CourseStudentRepository => _courseStudentRepository ??= new CourseStudentRepository(_context); + public ICustomerBalanceRepository CustomerBalanceRepository => + _customerBalanceRepository ??= new CustomerBalanceRepository(_context); + + public ITransactionHistoryRepository TransactionHistoryRepository => + _transactionHistoryRepository ??= new TransactionHistoryRepository(_context); + public void Dispose() { Dispose(true); diff --git a/WebApplication1/Controllers/TestController.cs b/WebApplication1/Controllers/TestController.cs index 3a92dde..5d4b888 100644 --- a/WebApplication1/Controllers/TestController.cs +++ b/WebApplication1/Controllers/TestController.cs @@ -10,16 +10,20 @@ namespace WebApplication1.Controllers public class TestController : MainApiController { private readonly ITestService _testService; + private readonly ITransactionService _transactionService; - public TestController(ITestService testService) + public TestController(ITestService testService, ITransactionService transactionService) { _testService = testService; + _transactionService = transactionService; } [HttpGet] public async Task Index() { - // await _testService.DummyData2(); + // await _testService.AddNewRoles(); + // await _testService.AddNewUser(); + await _transactionService.FinancialTransaction(); return Ok("hello world"); } } diff --git a/WebApplication1/Startup.cs b/WebApplication1/Startup.cs index 3af9e5b..63ab317 100644 --- a/WebApplication1/Startup.cs +++ b/WebApplication1/Startup.cs @@ -1,22 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using BLL; -using BLL.Request; using DLL; +using DLL.DBContext; +using DLL.Models; +using WebApplication1.Middlewares; using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using WebApplication1.Middlewares; +using Newtonsoft.Json; +using OpenIddict.Abstractions; +using Pomelo.EntityFrameworkCore.MySql.Infrastructure; +using Pomelo.EntityFrameworkCore.MySql.Storage; + namespace WebApplication1 { @@ -32,24 +40,158 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddControllers() - .AddFluentValidation() - .AddNewtonsoftJson(); - //.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining()); - ; + services.AddControllers().AddFluentValidation().AddNewtonsoftJson( + opt => opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); + + SetupSwagger(services); services.AddApiVersioning(config => { + // Specify the default API Version as 1.0 config.DefaultApiVersion = new ApiVersion(1, 0); + // If the client hasn't specified the API version in the request, use the default API version number config.AssumeDefaultVersionWhenUnspecified = true; - config.ReportApiVersions = true; - config.ApiVersionReader = new HeaderApiVersionReader("api-version"); }); + + + //DbConfiguration(services); + IdentitySetup(services); + //OpenIddictSetup(services); + + DLLDependency.AllDependency(services, Configuration); + BLLDependency.AllDependency(services, Configuration); + + + } + + private void DbConfiguration(IServiceCollection services) + { + // Replace with your server version and type. + // Use 'MariaDbServerVersion' for MariaDB. + // Alternatively, use 'ServerVersion.AutoDetect(connectionString)'. + // For common usages, see pull request #1233. + var serverVersion = new MySqlServerVersion(new Version(8, 0, 21)); + + services.AddDbContext(options => + options.UseOpenIddict() + .UseMySql(Configuration.GetConnectionString("DefaultConnection"), serverVersion) + ); + } + + private void OpenIddictSetup(IServiceCollection services) + { + services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext().ReplaceDefaultEntities(); + + + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the token endpoint. + options.SetTokenEndpointUris("/connect/token"); + + options.SetAccessTokenLifetime( + TimeSpan.FromMinutes(Configuration.GetValue("ProjectSetup:AccessTokenTime"))); + + // Enable the password flow. + options.AllowPasswordFlow().AllowRefreshTokenFlow(); + + // Accept anonymous clients (i.e clients that don't send a client_id). + // options.AcceptAnonymousClients(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableTokenEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + + services.Configure(options => + { + options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name; + options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject; + options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role; + }); + } + + private void IdentitySetup(IServiceCollection services) + { + services.AddIdentity() + .AddEntityFrameworkStores().AddDefaultTokenProviders(); + } + + private void SetupSwagger(IServiceCollection services) + { services.AddSwaggerGen(c => { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); + c.SwaggerDoc("v1", new OpenApiInfo + { + Version = "v1", + Title = "Practise Application API", + Description = "A simple example ASP.NET Core Web API", + TermsOfService = new Uri("https://example.com/terms"), + Contact = new OpenApiContact + { + Name = "Ghabi Assaad", + Email = "ghabiassaad@gmail.com", + Url = new Uri("https://twitter.com/spboyer"), + }, + License = new OpenApiLicense + { + Name = "Use under LICX", + Url = new Uri("https://example.com/license"), + } + }); + + //c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + //{ + // Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n + // Enter 'Bearer' [space] and then your token in the text input below. + // \r\n\r\nExample: 'Bearer 12345abcdef'", + // Name = "Authorization", + // In = ParameterLocation.Header, + // Type = SecuritySchemeType.ApiKey, + // Scheme = "Bearer" + //}); + + //c.AddSecurityRequirement(new OpenApiSecurityRequirement + //{ + // { + // new OpenApiSecurityScheme + // { + // Reference = new OpenApiReference + // { + // Type = ReferenceType.SecurityScheme, + // Id = "Bearer" + // }, + // Scheme = "oauth2", + // Name = "Bearer", + // In = ParameterLocation.Header + // }, + // new List() + // } + //}); }); - DLLDependency.AllDependency(services, Configuration); - BLLDependency.AllDependency(services); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -58,22 +200,35 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); } app.UseMiddleware(); + // Enable middleware to serve generated Swagger as a JSON endpoint. + app.UseSwagger(); + + // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), + // specifying the Swagger JSON endpoint. + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 V1"); + }); + app.UseHttpsRedirection(); app.UseRouting(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => + app.UseForwardedHeaders(new ForwardedHeadersOptions { - endpoints.MapControllers(); + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseStaticFiles(); + + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } } diff --git a/WebApplication1/WebApplication1.csproj b/WebApplication1/WebApplication1.csproj index f7c9374..6aea56e 100644 --- a/WebApplication1/WebApplication1.csproj +++ b/WebApplication1/WebApplication1.csproj @@ -1,4 +1,4 @@ - + net5.0 @@ -13,6 +13,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + + +