diff --git a/application/Directory.Packages.props b/application/Directory.Packages.props index 23114cb98..f4ea2367b 100644 --- a/application/Directory.Packages.props +++ b/application/Directory.Packages.props @@ -21,6 +21,7 @@ + diff --git a/application/account-management/Core/Database/AccountManagementDbContext.cs b/application/account-management/Core/Database/AccountManagementDbContext.cs index 129678040..5141ac3c9 100644 --- a/application/account-management/Core/Database/AccountManagementDbContext.cs +++ b/application/account-management/Core/Database/AccountManagementDbContext.cs @@ -1,49 +1,8 @@ using Microsoft.EntityFrameworkCore; -using PlatformPlatform.AccountManagement.Features.Authentication.Domain; -using PlatformPlatform.AccountManagement.Features.Signups.Domain; -using PlatformPlatform.AccountManagement.Features.Tenants.Domain; -using PlatformPlatform.AccountManagement.Features.Users.Domain; -using PlatformPlatform.SharedKernel.Domain; using PlatformPlatform.SharedKernel.EntityFramework; using PlatformPlatform.SharedKernel.ExecutionContext; namespace PlatformPlatform.AccountManagement.Database; public sealed class AccountManagementDbContext(DbContextOptions options, IExecutionContext executionContext) - : SharedKernelDbContext(options, executionContext) -{ - public DbSet Logins => Set(); - - public DbSet Signups => Set(); - - public DbSet Tenants => Set(); - - public DbSet Users => Set(); - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - // Login - modelBuilder.MapStronglyTypedId(t => t.Id); - modelBuilder.MapStronglyTypedId(u => u.TenantId); - modelBuilder.MapStronglyTypedUuid(u => u.UserId); - - // Signup - modelBuilder.MapStronglyTypedUuid(a => a.Id); - modelBuilder.MapStronglyTypedNullableId(u => u.TenantId); - - // Tenant - modelBuilder.MapStronglyTypedId(t => t.Id); - - // User - modelBuilder.MapStronglyTypedUuid(u => u.Id); - modelBuilder.MapStronglyTypedId(u => u.TenantId); - modelBuilder.Entity() - .OwnsOne(e => e.Avatar, b => b.ToJson()) - .HasOne() - .WithMany() - .HasForeignKey(u => u.TenantId) - .HasPrincipalKey(t => t.Id); - } -} + : SharedKernelDbContext(options, executionContext); diff --git a/application/account-management/Core/Features/Authentication/Domain/LoginConfiguration.cs b/application/account-management/Core/Features/Authentication/Domain/LoginConfiguration.cs new file mode 100644 index 000000000..dd6c812e5 --- /dev/null +++ b/application/account-management/Core/Features/Authentication/Domain/LoginConfiguration.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlatformPlatform.SharedKernel.Domain; +using PlatformPlatform.SharedKernel.EntityFramework; + +namespace PlatformPlatform.AccountManagement.Features.Authentication.Domain; + +public sealed class LoginConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.MapStronglyTypedId(t => t.Id); + builder.MapStronglyTypedId(u => u.TenantId); + builder.MapStronglyTypedUuid(u => u.UserId); + } +} diff --git a/application/account-management/Core/Features/Signups/Domain/SignupConfiguration.cs b/application/account-management/Core/Features/Signups/Domain/SignupConfiguration.cs new file mode 100644 index 000000000..2599dfbc6 --- /dev/null +++ b/application/account-management/Core/Features/Signups/Domain/SignupConfiguration.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlatformPlatform.SharedKernel.Domain; +using PlatformPlatform.SharedKernel.EntityFramework; + +namespace PlatformPlatform.AccountManagement.Features.Signups.Domain; + +public sealed class SignupConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.MapStronglyTypedUuid(a => a.Id); + builder.MapStronglyTypedNullableId(u => u.TenantId); + } +} diff --git a/application/account-management/Core/Features/Signups/Domain/SignupRepository.cs b/application/account-management/Core/Features/Signups/Domain/SignupRepository.cs index a44d5d836..9bb2fc4d1 100644 --- a/application/account-management/Core/Features/Signups/Domain/SignupRepository.cs +++ b/application/account-management/Core/Features/Signups/Domain/SignupRepository.cs @@ -14,7 +14,7 @@ public sealed class SignupRepository(AccountManagementDbContext accountManagemen { public Signup[] GetByEmailOrTenantId(TenantId tenantId, string email) { - return accountManagementDbContext.Signups + return DbSet .Where(r => !r.Completed) .Where(r => r.TenantId == tenantId || r.Email == email.ToLowerInvariant()) .ToArray(); diff --git a/application/account-management/Core/Features/Tenants/Domain/TenantConfiguration.cs b/application/account-management/Core/Features/Tenants/Domain/TenantConfiguration.cs new file mode 100644 index 000000000..d070fd1cf --- /dev/null +++ b/application/account-management/Core/Features/Tenants/Domain/TenantConfiguration.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlatformPlatform.SharedKernel.Domain; +using PlatformPlatform.SharedKernel.EntityFramework; + +namespace PlatformPlatform.AccountManagement.Features.Tenants.Domain; + +public sealed class TenantConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.MapStronglyTypedId(t => t.Id); + } +} diff --git a/application/account-management/Core/Features/Users/Domain/UserConfiguration.cs b/application/account-management/Core/Features/Users/Domain/UserConfiguration.cs new file mode 100644 index 000000000..13d414512 --- /dev/null +++ b/application/account-management/Core/Features/Users/Domain/UserConfiguration.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using PlatformPlatform.AccountManagement.Features.Tenants.Domain; +using PlatformPlatform.SharedKernel.Domain; +using PlatformPlatform.SharedKernel.EntityFramework; + +namespace PlatformPlatform.AccountManagement.Features.Users.Domain; + +public sealed class UserConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.MapStronglyTypedUuid(u => u.Id); + builder.MapStronglyTypedId(u => u.TenantId); + builder + .OwnsOne(e => e.Avatar, b => b.ToJson()) + .HasOne() + .WithMany() + .HasForeignKey(u => u.TenantId) + .HasPrincipalKey(t => t.Id); + } +} diff --git a/application/account-management/Tests/DatabaseSeeder.cs b/application/account-management/Tests/DatabaseSeeder.cs index 372e65aed..617813bf4 100644 --- a/application/account-management/Tests/DatabaseSeeder.cs +++ b/application/account-management/Tests/DatabaseSeeder.cs @@ -13,9 +13,9 @@ public sealed class DatabaseSeeder public DatabaseSeeder(AccountManagementDbContext accountManagementDbContext) { Tenant1 = Tenant.Create(new TenantId("tenant-1"), "owner@tenant-1.com"); - accountManagementDbContext.Tenants.AddRange(Tenant1); + accountManagementDbContext.Set().AddRange(Tenant1); User1 = User.Create(Tenant1.Id, "owner@tenant-1.com", UserRole.Owner, true, null); - accountManagementDbContext.Users.AddRange(User1); + accountManagementDbContext.Set().AddRange(User1); accountManagementDbContext.SaveChanges(); } diff --git a/application/shared-kernel/SharedKernel/EntityFramework/ModelBuilderExtensions.cs b/application/shared-kernel/SharedKernel/EntityFramework/ModelBuilderExtensions.cs index 4e56f1dd2..63509037c 100644 --- a/application/shared-kernel/SharedKernel/EntityFramework/ModelBuilderExtensions.cs +++ b/application/shared-kernel/SharedKernel/EntityFramework/ModelBuilderExtensions.cs @@ -1,5 +1,6 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using PlatformPlatform.SharedKernel.StronglyTypedIds; @@ -11,37 +12,34 @@ public static class ModelBuilderExtensions /// This method is used to tell Entity Framework how to map a strongly typed ID to a SQL column using the /// underlying type of the strongly-typed ID. /// - public static void MapStronglyTypedLongId(this ModelBuilder modelBuilder, Expression> expression) + public static void MapStronglyTypedLongId(this EntityTypeBuilder builder, Expression> expression) where T : class where TId : StronglyTypedLongId { - modelBuilder - .Entity() + builder .Property(expression) .HasConversion(v => v.Value, v => (Activator.CreateInstance(typeof(TId), v) as TId)!); } - public static void MapStronglyTypedUuid(this ModelBuilder modelBuilder, Expression> expression) + public static void MapStronglyTypedUuid(this EntityTypeBuilder builder, Expression> expression) where T : class where TId : StronglyTypedUlid { - modelBuilder - .Entity() + builder .Property(expression) .HasConversion(v => v.Value, v => (Activator.CreateInstance(typeof(TId), v) as TId)!); } - public static void MapStronglyTypedId(this ModelBuilder modelBuilder, Expression> expression) + public static void MapStronglyTypedId(this EntityTypeBuilder builder, Expression> expression) where T : class where TValue : IComparable where TId : StronglyTypedId { - modelBuilder - .Entity() + builder .Property(expression) .HasConversion(v => v.Value, v => (Activator.CreateInstance(typeof(TId), v) as TId)!); } public static void MapStronglyTypedNullableId( - this ModelBuilder modelBuilder, + this EntityTypeBuilder builder, Expression> idExpression ) where T : class @@ -54,8 +52,7 @@ public static void MapStronglyTypedNullableId( var idCoalesceExpression = Expression.Lambda>(Expression.Coalesce(idValueProperty, nullConstant), idParameter); - modelBuilder - .Entity() + builder .Property(idExpression) .HasConversion(idCoalesceExpression!, v => Activator.CreateInstance(typeof(TId), v) as TId); } diff --git a/application/shared-kernel/SharedKernel/EntityFramework/SharedKernelDbContext.cs b/application/shared-kernel/SharedKernel/EntityFramework/SharedKernelDbContext.cs index 3e94414b5..d7892993f 100644 --- a/application/shared-kernel/SharedKernel/EntityFramework/SharedKernelDbContext.cs +++ b/application/shared-kernel/SharedKernel/EntityFramework/SharedKernelDbContext.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using Humanizer; using Microsoft.EntityFrameworkCore; using PlatformPlatform.SharedKernel.Domain; using PlatformPlatform.SharedKernel.ExecutionContext; @@ -24,6 +25,15 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.ApplyConfigurationsFromAssembly(typeof(TContext).Assembly); + + // Set pluralized table names for all aggregates + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + var tableName = entityType.GetTableName()!.Pluralize(); + entityType.SetTableName(tableName); + } + // Ensures that all enum properties are stored as strings in the database. modelBuilder.UseStringForEnums(); diff --git a/application/shared-kernel/SharedKernel/SharedKernel.csproj b/application/shared-kernel/SharedKernel/SharedKernel.csproj index cf322645c..45a84e53e 100644 --- a/application/shared-kernel/SharedKernel/SharedKernel.csproj +++ b/application/shared-kernel/SharedKernel/SharedKernel.csproj @@ -23,6 +23,7 @@ +