Create models and Clean Arch boilerplate
This commit is contained in:
parent
3926db5446
commit
872dc1e263
21 changed files with 576 additions and 27 deletions
37
CSR.Infrastructure/Persistence/CSRDbContext.cs
Normal file
37
CSR.Infrastructure/Persistence/CSRDbContext.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
namespace CSR.Infrastructure.Persistence;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using CSR.Infrastructure.Persistence.Configurations;
|
||||
|
||||
|
||||
public class CSRDbContext(DbContextOptions<CSRDbContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<Role> Roles { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.ApplyConfiguration(new UserConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new RoleConfiguration());
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasIndex(u => u.Username)
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<User>()
|
||||
.HasOne(u => u.Role)
|
||||
.WithMany(r => r.Users)
|
||||
.HasForeignKey(u => u.RoleId);
|
||||
|
||||
// --- Seed data --- //
|
||||
|
||||
var adminRole = new Role { Id = 1, Name = "Admin" };
|
||||
var userRole = new Role { Id = 2, Name = "User" };
|
||||
|
||||
modelBuilder.Entity<Role>()
|
||||
.HasData(adminRole, userRole);
|
||||
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
namespace CSR.Infrastructure.Persistence.Configurations;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
|
||||
public class RoleConfiguration : IEntityTypeConfiguration<Role>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Role> builder)
|
||||
{
|
||||
builder.Property(u => u.Id)
|
||||
.HasColumnName("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(u => u.Name)
|
||||
.HasColumnName("RoleName")
|
||||
.IsRequired();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
namespace CSR.Infrastructure.Persistence.Configurations;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
|
||||
public class UserConfiguration : IEntityTypeConfiguration<User>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<User> builder)
|
||||
{
|
||||
builder.Property(u => u.PasswordHash)
|
||||
.HasColumnName("Password")
|
||||
.IsRequired();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
namespace Csr.Infrastructure.Persistence.Repositories;
|
||||
|
||||
using CSR.Domain.Entities;
|
||||
using CSR.Application.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class RoleRepository(CSR.Infrastructure.Persistence.CSRDbContext context) : IRoleRepository
|
||||
{
|
||||
private readonly CSR.Infrastructure.Persistence.CSRDbContext _context = context;
|
||||
|
||||
public async Task<Role?> GetByIdAsync(int id)
|
||||
{
|
||||
var roleEntity = await _context.Roles
|
||||
.Include(r => r.Id)
|
||||
.SingleOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (roleEntity == null)
|
||||
{
|
||||
return null; // No entity found, return null domain model
|
||||
}
|
||||
|
||||
var role = Role.LoadExisting(
|
||||
roleEntity.Id,
|
||||
roleEntity.Name
|
||||
);
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
|
||||
public async Task AddAsync(Role role)
|
||||
{
|
||||
var roleEntity = new CSR.Infrastructure.Persistence.Role
|
||||
{
|
||||
Id = role.Id,
|
||||
Name = role.Name
|
||||
};
|
||||
|
||||
_context.Roles.Add(roleEntity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var roleEntity = new CSR.Infrastructure.Persistence.Role
|
||||
{
|
||||
Id = id,
|
||||
Name = string.Empty
|
||||
};
|
||||
|
||||
_context.Roles.Remove(roleEntity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
105
CSR.Infrastructure/Persistence/Repositories/UserRepository.cs
Normal file
105
CSR.Infrastructure/Persistence/Repositories/UserRepository.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
namespace Csr.Infrastructure.Persistence.Repositories;
|
||||
|
||||
using CSR.Domain.Entities;
|
||||
using CSR.Application.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class UserRepository(CSR.Infrastructure.Persistence.CSRDbContext context) : IUserRepository
|
||||
{
|
||||
private readonly CSR.Infrastructure.Persistence.CSRDbContext _context = context;
|
||||
|
||||
public async Task<User?> GetByIdAsync(int id)
|
||||
{
|
||||
var userEntity = await _context.Users
|
||||
.Include(u => u.Role)
|
||||
.SingleOrDefaultAsync(u => u.Id == id);
|
||||
|
||||
if (userEntity == null)
|
||||
{
|
||||
return null; // No entity found, return null domain model
|
||||
}
|
||||
|
||||
var user = User.LoadExisting(
|
||||
userEntity.Id,
|
||||
userEntity.Username,
|
||||
userEntity.Email,
|
||||
userEntity.PasswordHash,
|
||||
userEntity.RoleId,
|
||||
Role.LoadExisting(
|
||||
userEntity.Role.Id,
|
||||
userEntity.Role.Name
|
||||
)
|
||||
);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
public async Task AddAsync(User user)
|
||||
{
|
||||
var userEntity = new CSR.Infrastructure.Persistence.User
|
||||
{
|
||||
Username = user.Username,
|
||||
Email = user.Email,
|
||||
PasswordHash = user.PasswordHash,
|
||||
RoleId = user.RoleId,
|
||||
Role = new CSR.Infrastructure.Persistence.Role
|
||||
{
|
||||
Id = user.Role.Id,
|
||||
Name = user.Role.Name
|
||||
}
|
||||
};
|
||||
|
||||
_context.Users.Add(userEntity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task UpdateAsync(User user)
|
||||
{
|
||||
var userEntity = await _context.Users
|
||||
.Include(u => u.Role)
|
||||
.FirstOrDefaultAsync(u => u.Id == user.Id);
|
||||
|
||||
if (userEntity == null)
|
||||
{
|
||||
// NOTE should I throw an exception here?
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
userEntity.Id = user.Id;
|
||||
userEntity.Username = user.Username;
|
||||
userEntity.Email = user.Email;
|
||||
userEntity.PasswordHash = user.PasswordHash;
|
||||
userEntity.RoleId = user.RoleId;
|
||||
userEntity.Role = new CSR.Infrastructure.Persistence.Role { Id = user.Role.Id, Name = user.Role.Name };
|
||||
|
||||
// Prevent EF from trying to update the Role entity
|
||||
_context.Entry(userEntity.Role).State = EntityState.Unchanged;
|
||||
|
||||
_context.Users.Update(userEntity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var userEntity = new CSR.Infrastructure.Persistence.User
|
||||
{
|
||||
Id = id,
|
||||
Username = string.Empty,
|
||||
Email = string.Empty,
|
||||
PasswordHash = string.Empty,
|
||||
RoleId = 0,
|
||||
Role = new CSR.Infrastructure.Persistence.Role
|
||||
{
|
||||
Id = 0,
|
||||
Name = string.Empty
|
||||
}
|
||||
};
|
||||
|
||||
_context.Users.Remove(userEntity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
10
CSR.Infrastructure/Persistence/Role.cs
Normal file
10
CSR.Infrastructure/Persistence/Role.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace CSR.Infrastructure.Persistence;
|
||||
|
||||
public class Role
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
|
||||
// Navigation property
|
||||
public ICollection<User> Users { get; set; } = new HashSet<User>();
|
||||
}
|
17
CSR.Infrastructure/Persistence/User.cs
Normal file
17
CSR.Infrastructure/Persistence/User.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace CSR.Infrastructure.Persistence;
|
||||
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public required string Username { get; set; }
|
||||
public required string Email { get; set; }
|
||||
public required string PasswordHash { get; set; }
|
||||
public required int RoleId { get; set; }
|
||||
|
||||
// Navigation property
|
||||
public Role Role { get; set; } = null!;
|
||||
|
||||
// prevent direct instantiation
|
||||
// use UserService to create a new user
|
||||
internal User() { }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue