Create models and Clean Arch boilerplate

This commit is contained in:
danial23 2025-05-19 16:13:58 -04:00
parent 3926db5446
commit 872dc1e263
Signed by: danial23
SSH key fingerprint: SHA256:IJ8VP0j2WMUVweTYnzUUnEjNgPnGx+mAt+RhqWZ01bU
21 changed files with 576 additions and 27 deletions

View file

@ -2,6 +2,14 @@
<ItemGroup>
<ProjectReference Include="..\CSR.Application\CSR.Application.csproj" />
<ProjectReference Include="..\CSR.Domain\CSR.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>

View file

@ -1,6 +0,0 @@
namespace CSR.Infrastructure;
public class Class1
{
}

View 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);
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View 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();
}
}

View 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>();
}

View 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() { }
}