UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,574 lines (1,249 loc) 85.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.aspnetEFCoreTemplate = void 0; exports.aspnetEFCoreTemplate = { id: 'aspnet-efcore', name: 'aspnet-efcore', displayName: 'ASP.NET Core with Entity Framework Core', description: 'Enterprise .NET API with EF Core migrations and advanced database patterns', language: 'csharp', framework: 'aspnet-efcore', version: '1.0.0', tags: ['aspnet', 'efcore', 'migrations', 'database', 'orm'], port: 5000, dependencies: {}, features: ['authentication', 'database', 'validation', 'logging', 'testing'], files: { // Project file '{{serviceName}}.csproj': `<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.0" /> <PackageReference Include="AutoMapper" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" /> <PackageReference Include="Serilog.AspNetCore" Version="8.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" /> </ItemGroup> </Project>`, // Program.cs 'Program.cs': `using {{serviceName}}.Data; using {{serviceName}}.Services; using {{serviceName}}.Models; using {{serviceName}}.DTOs; using {{serviceName}}.Profiles; using {{serviceName}}.Validators; using {{serviceName}}.Infrastructure; using Microsoft.EntityFrameworkCore; using AutoMapper; using FluentValidation; using Serilog; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; var builder = WebApplication.CreateBuilder(args); // Configure Serilog Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .Enrich.FromLogContext() .WriteTo.Console() .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); builder.Host.UseSerilog(); // Add services to the container builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); // Configure Entity Framework with connection string selection var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); if (builder.Environment.IsDevelopment()) { connectionString = builder.Configuration.GetConnectionString("DevelopmentConnection") ?? connectionString; } builder.Services.AddDbContext<ApplicationDbContext>(options => { if (builder.Environment.IsEnvironment("Testing")) { options.UseInMemoryDatabase("TestDb"); } else { options.UseSqlServer(connectionString, sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(5), errorNumbersToAdd: null); sqlOptions.CommandTimeout(30); }); // Enable sensitive data logging in development if (builder.Environment.IsDevelopment()) { options.EnableSensitiveDataLogging(); options.EnableDetailedErrors(); } } }); // Configure AutoMapper builder.Services.AddAutoMapper(typeof(UserProfile), typeof(ProductProfile), typeof(OrderProfile)); // Configure FluentValidation builder.Services.AddValidatorsFromAssemblyContaining<CreateUserDtoValidator>(); // Configure Swagger/OpenAPI builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "{{serviceName}} API", Version = "v1", Description = "Enterprise API with Entity Framework Core and migrations", Contact = new OpenApiContact { Name = "{{serviceName}} Team", Email = "team@{{serviceName}}.com" } }); // Include XML comments var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); if (File.Exists(xmlPath)) { c.IncludeXmlComments(xmlPath); } // Configure JWT authentication in Swagger c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme", 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<string>() } }); }); // Configure JWT Authentication var jwtSettings = builder.Configuration.GetSection("JwtSettings"); var secretKey = jwtSettings["SecretKey"]; builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings["Issuer"], ValidAudience = jwtSettings["Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey!)) }; }); // Register application services builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IProductService, ProductService>(); builder.Services.AddScoped<IOrderService, OrderService>(); builder.Services.AddScoped<IAuthService, AuthService>(); builder.Services.AddScoped<ICategoryService, CategoryService>(); builder.Services.AddScoped<IAuditService, AuditService>(); // Register infrastructure services builder.Services.AddScoped<IDatabaseSeeder, DatabaseSeeder>(); builder.Services.AddScoped<IMigrationService, MigrationService>(); var app = builder.Build(); // Configure the HTTP request pipeline if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); // Database initialization and migration (skip in testing environment) if (!app.Environment.IsEnvironment("Testing")) { using var scope = app.Services.CreateScope(); var migrationService = scope.ServiceProvider.GetRequiredService<IMigrationService>(); // Apply pending migrations await migrationService.ApplyMigrationsAsync(); // Seed initial data if needed if (app.Environment.IsDevelopment()) { var seeder = scope.ServiceProvider.GetRequiredService<IDatabaseSeeder>(); await seeder.SeedAsync(); } } app.Run(); // Make the implicit Program class public for testing public partial class Program { }`, // Models/User.cs 'Models/User.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(Email), IsUnique = true)] [Index(nameof(CreatedAt))] public class User : BaseEntity { [Required] [StringLength(100)] public string Name { get; set; } = string.Empty; [Required] [EmailAddress] [StringLength(255)] public string Email { get; set; } = string.Empty; [Required] public string PasswordHash { get; set; } = string.Empty; [StringLength(20)] public string? PhoneNumber { get; set; } public DateTime DateOfBirth { get; set; } [StringLength(500)] public string? Address { get; set; } [StringLength(100)] public string? City { get; set; } [StringLength(100)] public string? Country { get; set; } [StringLength(10)] public string? PostalCode { get; set; } public bool IsActive { get; set; } = true; public bool IsEmailVerified { get; set; } = false; public DateTime? LastLoginAt { get; set; } // Navigation properties public virtual ICollection<Order> Orders { get; set; } = new List<Order>(); public virtual ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>(); public virtual ICollection<AuditLog> AuditLogs { get; set; } = new List<AuditLog>(); }`, // Models/Product.cs 'Models/Product.cs': `using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(Name))] [Index(nameof(CategoryId))] [Index(nameof(Price))] [Index(nameof(CreatedAt))] public class Product : BaseEntity { [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } [Required] [Column(TypeName = "decimal(18,2)")] [Range(0.01, 999999.99)] public decimal Price { get; set; } [Required] public int CategoryId { get; set; } [StringLength(50)] public string? Brand { get; set; } [StringLength(50)] public string? SKU { get; set; } public int StockQuantity { get; set; } public int MinStockLevel { get; set; } = 0; [StringLength(500)] public string? ImageUrl { get; set; } [Column(TypeName = "decimal(5,2)")] [Range(0, 100)] public decimal? DiscountPercentage { get; set; } public bool IsActive { get; set; } = true; public bool IsFeatured { get; set; } = false; [Column(TypeName = "decimal(3,2)")] [Range(0, 5)] public decimal? Rating { get; set; } public int ReviewCount { get; set; } = 0; public DateTime? DiscountStartDate { get; set; } public DateTime? DiscountEndDate { get; set; } // Navigation properties public virtual Category Category { get; set; } = null!; public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>(); public virtual ICollection<ProductTag> ProductTags { get; set; } = new List<ProductTag>(); public virtual ICollection<ProductImage> ProductImages { get; set; } = new List<ProductImage>(); }`, // Models/Category.cs 'Models/Category.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(Name), IsUnique = true)] [Index(nameof(ParentCategoryId))] public class Category : BaseEntity { [Required] [StringLength(100)] public string Name { get; set; } = string.Empty; [StringLength(500)] public string? Description { get; set; } [StringLength(100)] public string? Slug { get; set; } public int? ParentCategoryId { get; set; } [StringLength(500)] public string? ImageUrl { get; set; } public bool IsActive { get; set; } = true; public int SortOrder { get; set; } = 0; // Navigation properties public virtual Category? ParentCategory { get; set; } public virtual ICollection<Category> SubCategories { get; set; } = new List<Category>(); public virtual ICollection<Product> Products { get; set; } = new List<Product>(); }`, // Models/Order.cs 'Models/Order.cs': `using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(OrderNumber), IsUnique = true)] [Index(nameof(UserId))] [Index(nameof(Status))] [Index(nameof(CreatedAt))] public class Order : BaseEntity { [Required] public int UserId { get; set; } [Required] [StringLength(50)] public string OrderNumber { get; set; } = string.Empty; [Required] [Column(TypeName = "decimal(18,2)")] public decimal SubtotalAmount { get; set; } [Column(TypeName = "decimal(18,2)")] public decimal TaxAmount { get; set; } = 0; [Column(TypeName = "decimal(18,2)")] public decimal ShippingAmount { get; set; } = 0; [Column(TypeName = "decimal(18,2)")] public decimal DiscountAmount { get; set; } = 0; [Required] [Column(TypeName = "decimal(18,2)")] public decimal TotalAmount { get; set; } [Required] [StringLength(50)] public string Status { get; set; } = "Pending"; [StringLength(50)] public string? PaymentStatus { get; set; } = "Pending"; [StringLength(50)] public string? PaymentMethod { get; set; } [StringLength(100)] public string? PaymentTransactionId { get; set; } [StringLength(500)] public string? ShippingAddress { get; set; } [StringLength(100)] public string? ShippingCity { get; set; } [StringLength(100)] public string? ShippingCountry { get; set; } [StringLength(20)] public string? ShippingPostalCode { get; set; } [StringLength(500)] public string? BillingAddress { get; set; } [StringLength(100)] public string? BillingCity { get; set; } [StringLength(100)] public string? BillingCountry { get; set; } [StringLength(20)] public string? BillingPostalCode { get; set; } [StringLength(1000)] public string? Notes { get; set; } public DateTime? ShippedAt { get; set; } public DateTime? DeliveredAt { get; set; } [StringLength(100)] public string? TrackingNumber { get; set; } // Navigation properties public virtual User User { get; set; } = null!; public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>(); public virtual ICollection<OrderStatusHistory> StatusHistory { get; set; } = new List<OrderStatusHistory>(); }`, // Models/OrderItem.cs 'Models/OrderItem.cs': `using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(OrderId))] [Index(nameof(ProductId))] public class OrderItem : BaseEntity { [Required] public int OrderId { get; set; } [Required] public int ProductId { get; set; } [Required] [Range(1, int.MaxValue)] public int Quantity { get; set; } [Required] [Column(TypeName = "decimal(18,2)")] public decimal UnitPrice { get; set; } [Column(TypeName = "decimal(18,2)")] public decimal DiscountAmount { get; set; } = 0; [Required] [Column(TypeName = "decimal(18,2)")] public decimal TotalPrice { get; set; } [StringLength(200)] public string? ProductName { get; set; } // Snapshot of product name at time of order [StringLength(50)] public string? ProductSKU { get; set; } // Snapshot of product SKU at time of order // Navigation properties public virtual Order Order { get; set; } = null!; public virtual Product Product { get; set; } = null!; }`, // Models/BaseEntity.cs 'Models/BaseEntity.cs': `using System.ComponentModel.DataAnnotations; namespace {{serviceName}}.Models; public abstract class BaseEntity { [Key] public int Id { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } [StringLength(100)] public string? CreatedBy { get; set; } [StringLength(100)] public string? UpdatedBy { get; set; } [Timestamp] public byte[]? RowVersion { get; set; } }`, // Models/UserRole.cs 'Models/UserRole.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(UserId), nameof(Role), IsUnique = true)] public class UserRole : BaseEntity { [Required] public int UserId { get; set; } [Required] [StringLength(50)] public string Role { get; set; } = string.Empty; [StringLength(200)] public string? Description { get; set; } public bool IsActive { get; set; } = true; public DateTime? ExpiresAt { get; set; } // Navigation properties public virtual User User { get; set; } = null!; }`, // Models/ProductTag.cs 'Models/ProductTag.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(ProductId), nameof(Tag), IsUnique = true)] public class ProductTag : BaseEntity { [Required] public int ProductId { get; set; } [Required] [StringLength(50)] public string Tag { get; set; } = string.Empty; // Navigation properties public virtual Product Product { get; set; } = null!; }`, // Models/ProductImage.cs 'Models/ProductImage.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(ProductId))] public class ProductImage : BaseEntity { [Required] public int ProductId { get; set; } [Required] [StringLength(500)] public string ImageUrl { get; set; } = string.Empty; [StringLength(200)] public string? AltText { get; set; } public bool IsPrimary { get; set; } = false; public int SortOrder { get; set; } = 0; // Navigation properties public virtual Product Product { get; set; } = null!; }`, // Models/OrderStatusHistory.cs 'Models/OrderStatusHistory.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(OrderId))] [Index(nameof(CreatedAt))] public class OrderStatusHistory : BaseEntity { [Required] public int OrderId { get; set; } [Required] [StringLength(50)] public string FromStatus { get; set; } = string.Empty; [Required] [StringLength(50)] public string ToStatus { get; set; } = string.Empty; [StringLength(500)] public string? Notes { get; set; } [StringLength(100)] public string? ChangedBy { get; set; } // Navigation properties public virtual Order Order { get; set; } = null!; }`, // Models/AuditLog.cs 'Models/AuditLog.cs': `using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace {{serviceName}}.Models; [Index(nameof(UserId))] [Index(nameof(EntityType))] [Index(nameof(Action))] [Index(nameof(CreatedAt))] public class AuditLog : BaseEntity { public int? UserId { get; set; } [Required] [StringLength(100)] public string EntityType { get; set; } = string.Empty; [Required] public string EntityId { get; set; } = string.Empty; [Required] [StringLength(50)] public string Action { get; set; } = string.Empty; // Create, Update, Delete [StringLength(100)] public string? UserEmail { get; set; } [StringLength(45)] public string? IPAddress { get; set; } [StringLength(500)] public string? UserAgent { get; set; } public string? OldValues { get; set; } // JSON public string? NewValues { get; set; } // JSON [StringLength(500)] public string? Changes { get; set; } // Summary of changes // Navigation properties public virtual User? User { get; set; } }`, // Data/ApplicationDbContext.cs 'Data/ApplicationDbContext.cs': `using Microsoft.EntityFrameworkCore; using {{serviceName}}.Models; using {{serviceName}}.Data.Configurations; namespace {{serviceName}}.Data; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } // DbSets public DbSet<User> Users { get; set; } public DbSet<Product> Products { get; set; } public DbSet<Category> Categories { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderItem> OrderItems { get; set; } public DbSet<UserRole> UserRoles { get; set; } public DbSet<ProductTag> ProductTags { get; set; } public DbSet<ProductImage> ProductImages { get; set; } public DbSet<OrderStatusHistory> OrderStatusHistory { get; set; } public DbSet<AuditLog> AuditLogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Apply entity configurations modelBuilder.ApplyConfiguration(new UserConfiguration()); modelBuilder.ApplyConfiguration(new ProductConfiguration()); modelBuilder.ApplyConfiguration(new CategoryConfiguration()); modelBuilder.ApplyConfiguration(new OrderConfiguration()); modelBuilder.ApplyConfiguration(new OrderItemConfiguration()); modelBuilder.ApplyConfiguration(new UserRoleConfiguration()); modelBuilder.ApplyConfiguration(new ProductTagConfiguration()); modelBuilder.ApplyConfiguration(new ProductImageConfiguration()); modelBuilder.ApplyConfiguration(new OrderStatusHistoryConfiguration()); modelBuilder.ApplyConfiguration(new AuditLogConfiguration()); // Global query filters modelBuilder.Entity<User>().HasQueryFilter(u => !u.IsDeleted); modelBuilder.Entity<Product>().HasQueryFilter(p => !p.IsDeleted); modelBuilder.Entity<Category>().HasQueryFilter(c => !c.IsDeleted); modelBuilder.Entity<Order>().HasQueryFilter(o => !o.IsDeleted); // Configure decimal precision globally foreach (var property in modelBuilder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?))) { property.SetColumnType("decimal(18,2)"); } // Set default values modelBuilder.Entity<User>() .Property(u => u.CreatedAt) .HasDefaultValueSql("GETUTCDATE()"); modelBuilder.Entity<Product>() .Property(p => p.CreatedAt) .HasDefaultValueSql("GETUTCDATE()"); modelBuilder.Entity<Order>() .Property(o => o.CreatedAt) .HasDefaultValueSql("GETUTCDATE()"); // Seed initial data SeedInitialData(modelBuilder); } public override int SaveChanges() { AddTimestamps(); return base.SaveChanges(); } public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { AddTimestamps(); return await base.SaveChangesAsync(cancellationToken); } private void AddTimestamps() { var entities = ChangeTracker.Entries() .Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified)); foreach (var entity in entities) { var baseEntity = (BaseEntity)entity.Entity; if (entity.State == EntityState.Added) { baseEntity.CreatedAt = DateTime.UtcNow; } else if (entity.State == EntityState.Modified) { baseEntity.UpdatedAt = DateTime.UtcNow; } } } private static void SeedInitialData(ModelBuilder modelBuilder) { // Seed Categories modelBuilder.Entity<Category>().HasData( new Category { Id = 1, Name = "Electronics", Description = "Electronic devices and gadgets", Slug = "electronics", IsActive = true, CreatedAt = DateTime.UtcNow }, new Category { Id = 2, Name = "Computers", Description = "Computers and accessories", Slug = "computers", ParentCategoryId = 1, IsActive = true, CreatedAt = DateTime.UtcNow }, new Category { Id = 3, Name = "Smartphones", Description = "Mobile phones and accessories", Slug = "smartphones", ParentCategoryId = 1, IsActive = true, CreatedAt = DateTime.UtcNow }, new Category { Id = 4, Name = "Clothing", Description = "Apparel and fashion", Slug = "clothing", IsActive = true, CreatedAt = DateTime.UtcNow }, new Category { Id = 5, Name = "Books", Description = "Books and literature", Slug = "books", IsActive = true, CreatedAt = DateTime.UtcNow } ); // Seed Products modelBuilder.Entity<Product>().HasData( new Product { Id = 1, Name = "Laptop Pro 15", Description = "High-performance laptop for professionals", Price = 1299.99m, CategoryId = 2, Brand = "TechCorp", SKU = "LAP-PRO-15", StockQuantity = 50, MinStockLevel = 10, ImageUrl = "/images/laptop-pro-15.jpg", IsActive = true, IsFeatured = true, Rating = 4.5m, ReviewCount = 128, CreatedAt = DateTime.UtcNow }, new Product { Id = 2, Name = "Wireless Mouse", Description = "Ergonomic wireless mouse with precision tracking", Price = 49.99m, CategoryId = 2, Brand = "TechCorp", SKU = "MSE-WRL-001", StockQuantity = 200, MinStockLevel = 20, ImageUrl = "/images/wireless-mouse.jpg", IsActive = true, Rating = 4.2m, ReviewCount = 89, CreatedAt = DateTime.UtcNow }, new Product { Id = 3, Name = "Smartphone X", Description = "Latest smartphone with advanced features", Price = 799.99m, CategoryId = 3, Brand = "MobileTech", SKU = "PHN-X-128", StockQuantity = 75, MinStockLevel = 15, ImageUrl = "/images/smartphone-x.jpg", IsActive = true, IsFeatured = true, Rating = 4.7m, ReviewCount = 234, CreatedAt = DateTime.UtcNow } ); } } // Extension for soft delete public static class ApplicationDbContextExtensions { public static void AddSoftDeleteProperty(this ModelBuilder modelBuilder) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType)) { entityType.AddProperty("IsDeleted", typeof(bool)); } } } }`, // Data/Configurations/UserConfiguration.cs 'Data/Configurations/UserConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class UserConfiguration : IEntityTypeConfiguration<User> { public void Configure(EntityTypeBuilder<User> builder) { builder.ToTable("Users"); builder.HasKey(u => u.Id); builder.Property(u => u.Name) .IsRequired() .HasMaxLength(100); builder.Property(u => u.Email) .IsRequired() .HasMaxLength(255); builder.Property(u => u.PasswordHash) .IsRequired(); builder.Property(u => u.PhoneNumber) .HasMaxLength(20); builder.Property(u => u.Address) .HasMaxLength(500); builder.Property(u => u.City) .HasMaxLength(100); builder.Property(u => u.Country) .HasMaxLength(100); builder.Property(u => u.PostalCode) .HasMaxLength(10); // Indexes builder.HasIndex(u => u.Email) .IsUnique() .HasDatabaseName("IX_Users_Email"); builder.HasIndex(u => u.CreatedAt) .HasDatabaseName("IX_Users_CreatedAt"); builder.HasIndex(u => u.IsActive) .HasDatabaseName("IX_Users_IsActive"); // Relationships builder.HasMany(u => u.Orders) .WithOne(o => o.User) .HasForeignKey(o => o.UserId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(u => u.UserRoles) .WithOne(ur => ur.User) .HasForeignKey(ur => ur.UserId) .OnDelete(DeleteBehavior.Cascade); builder.HasMany(u => u.AuditLogs) .WithOne(al => al.User) .HasForeignKey(al => al.UserId) .OnDelete(DeleteBehavior.SetNull); } }`, // Data/Configurations/ProductConfiguration.cs 'Data/Configurations/ProductConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class ProductConfiguration : IEntityTypeConfiguration<Product> { public void Configure(EntityTypeBuilder<Product> builder) { builder.ToTable("Products"); builder.HasKey(p => p.Id); builder.Property(p => p.Name) .IsRequired() .HasMaxLength(200); builder.Property(p => p.Description) .HasMaxLength(1000); builder.Property(p => p.Price) .IsRequired() .HasColumnType("decimal(18,2)"); builder.Property(p => p.Brand) .HasMaxLength(50); builder.Property(p => p.SKU) .HasMaxLength(50); builder.Property(p => p.ImageUrl) .HasMaxLength(500); builder.Property(p => p.DiscountPercentage) .HasColumnType("decimal(5,2)"); builder.Property(p => p.Rating) .HasColumnType("decimal(3,2)"); // Indexes builder.HasIndex(p => p.Name) .HasDatabaseName("IX_Products_Name"); builder.HasIndex(p => p.CategoryId) .HasDatabaseName("IX_Products_CategoryId"); builder.HasIndex(p => p.Price) .HasDatabaseName("IX_Products_Price"); builder.HasIndex(p => p.SKU) .IsUnique() .HasDatabaseName("IX_Products_SKU"); builder.HasIndex(p => p.IsActive) .HasDatabaseName("IX_Products_IsActive"); builder.HasIndex(p => p.IsFeatured) .HasDatabaseName("IX_Products_IsFeatured"); // Relationships builder.HasOne(p => p.Category) .WithMany(c => c.Products) .HasForeignKey(p => p.CategoryId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(p => p.OrderItems) .WithOne(oi => oi.Product) .HasForeignKey(oi => oi.ProductId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(p => p.ProductTags) .WithOne(pt => pt.Product) .HasForeignKey(pt => pt.ProductId) .OnDelete(DeleteBehavior.Cascade); builder.HasMany(p => p.ProductImages) .WithOne(pi => pi.Product) .HasForeignKey(pi => pi.ProductId) .OnDelete(DeleteBehavior.Cascade); // Check constraints builder.HasCheckConstraint("CK_Products_Price", "Price > 0"); builder.HasCheckConstraint("CK_Products_StockQuantity", "StockQuantity >= 0"); builder.HasCheckConstraint("CK_Products_MinStockLevel", "MinStockLevel >= 0"); builder.HasCheckConstraint("CK_Products_DiscountPercentage", "DiscountPercentage >= 0 AND DiscountPercentage <= 100"); builder.HasCheckConstraint("CK_Products_Rating", "Rating >= 0 AND Rating <= 5"); } }`, // Data/Configurations/CategoryConfiguration.cs 'Data/Configurations/CategoryConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class CategoryConfiguration : IEntityTypeConfiguration<Category> { public void Configure(EntityTypeBuilder<Category> builder) { builder.ToTable("Categories"); builder.HasKey(c => c.Id); builder.Property(c => c.Name) .IsRequired() .HasMaxLength(100); builder.Property(c => c.Description) .HasMaxLength(500); builder.Property(c => c.Slug) .HasMaxLength(100); builder.Property(c => c.ImageUrl) .HasMaxLength(500); // Indexes builder.HasIndex(c => c.Name) .IsUnique() .HasDatabaseName("IX_Categories_Name"); builder.HasIndex(c => c.ParentCategoryId) .HasDatabaseName("IX_Categories_ParentCategoryId"); builder.HasIndex(c => c.Slug) .IsUnique() .HasDatabaseName("IX_Categories_Slug"); // Self-referencing relationship builder.HasOne(c => c.ParentCategory) .WithMany(c => c.SubCategories) .HasForeignKey(c => c.ParentCategoryId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(c => c.Products) .WithOne(p => p.Category) .HasForeignKey(p => p.CategoryId) .OnDelete(DeleteBehavior.Restrict); } }`, // Data/Configurations/OrderConfiguration.cs 'Data/Configurations/OrderConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class OrderConfiguration : IEntityTypeConfiguration<Order> { public void Configure(EntityTypeBuilder<Order> builder) { builder.ToTable("Orders"); builder.HasKey(o => o.Id); builder.Property(o => o.OrderNumber) .IsRequired() .HasMaxLength(50); builder.Property(o => o.SubtotalAmount) .IsRequired() .HasColumnType("decimal(18,2)"); builder.Property(o => o.TaxAmount) .HasColumnType("decimal(18,2)"); builder.Property(o => o.ShippingAmount) .HasColumnType("decimal(18,2)"); builder.Property(o => o.DiscountAmount) .HasColumnType("decimal(18,2)"); builder.Property(o => o.TotalAmount) .IsRequired() .HasColumnType("decimal(18,2)"); builder.Property(o => o.Status) .IsRequired() .HasMaxLength(50) .HasDefaultValue("Pending"); builder.Property(o => o.PaymentStatus) .HasMaxLength(50) .HasDefaultValue("Pending"); builder.Property(o => o.PaymentMethod) .HasMaxLength(50); builder.Property(o => o.PaymentTransactionId) .HasMaxLength(100); builder.Property(o => o.ShippingAddress) .HasMaxLength(500); builder.Property(o => o.ShippingCity) .HasMaxLength(100); builder.Property(o => o.ShippingCountry) .HasMaxLength(100); builder.Property(o => o.ShippingPostalCode) .HasMaxLength(20); builder.Property(o => o.BillingAddress) .HasMaxLength(500); builder.Property(o => o.BillingCity) .HasMaxLength(100); builder.Property(o => o.BillingCountry) .HasMaxLength(100); builder.Property(o => o.BillingPostalCode) .HasMaxLength(20); builder.Property(o => o.Notes) .HasMaxLength(1000); builder.Property(o => o.TrackingNumber) .HasMaxLength(100); // Indexes builder.HasIndex(o => o.OrderNumber) .IsUnique() .HasDatabaseName("IX_Orders_OrderNumber"); builder.HasIndex(o => o.UserId) .HasDatabaseName("IX_Orders_UserId"); builder.HasIndex(o => o.Status) .HasDatabaseName("IX_Orders_Status"); builder.HasIndex(o => o.CreatedAt) .HasDatabaseName("IX_Orders_CreatedAt"); builder.HasIndex(o => o.PaymentStatus) .HasDatabaseName("IX_Orders_PaymentStatus"); // Relationships builder.HasOne(o => o.User) .WithMany(u => u.Orders) .HasForeignKey(o => o.UserId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(o => o.OrderItems) .WithOne(oi => oi.Order) .HasForeignKey(oi => oi.OrderId) .OnDelete(DeleteBehavior.Cascade); builder.HasMany(o => o.StatusHistory) .WithOne(osh => osh.Order) .HasForeignKey(osh => osh.OrderId) .OnDelete(DeleteBehavior.Cascade); // Check constraints builder.HasCheckConstraint("CK_Orders_SubtotalAmount", "SubtotalAmount >= 0"); builder.HasCheckConstraint("CK_Orders_TaxAmount", "TaxAmount >= 0"); builder.HasCheckConstraint("CK_Orders_ShippingAmount", "ShippingAmount >= 0"); builder.HasCheckConstraint("CK_Orders_DiscountAmount", "DiscountAmount >= 0"); builder.HasCheckConstraint("CK_Orders_TotalAmount", "TotalAmount >= 0"); } }`, // Data/Configurations/OrderItemConfiguration.cs 'Data/Configurations/OrderItemConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class OrderItemConfiguration : IEntityTypeConfiguration<OrderItem> { public void Configure(EntityTypeBuilder<OrderItem> builder) { builder.ToTable("OrderItems"); builder.HasKey(oi => oi.Id); builder.Property(oi => oi.Quantity) .IsRequired(); builder.Property(oi => oi.UnitPrice) .IsRequired() .HasColumnType("decimal(18,2)"); builder.Property(oi => oi.DiscountAmount) .HasColumnType("decimal(18,2)"); builder.Property(oi => oi.TotalPrice) .IsRequired() .HasColumnType("decimal(18,2)"); builder.Property(oi => oi.ProductName) .HasMaxLength(200); builder.Property(oi => oi.ProductSKU) .HasMaxLength(50); // Indexes builder.HasIndex(oi => oi.OrderId) .HasDatabaseName("IX_OrderItems_OrderId"); builder.HasIndex(oi => oi.ProductId) .HasDatabaseName("IX_OrderItems_ProductId"); // Relationships builder.HasOne(oi => oi.Order) .WithMany(o => o.OrderItems) .HasForeignKey(oi => oi.OrderId) .OnDelete(DeleteBehavior.Cascade); builder.HasOne(oi => oi.Product) .WithMany(p => p.OrderItems) .HasForeignKey(oi => oi.ProductId) .OnDelete(DeleteBehavior.Restrict); // Check constraints builder.HasCheckConstraint("CK_OrderItems_Quantity", "Quantity > 0"); builder.HasCheckConstraint("CK_OrderItems_UnitPrice", "UnitPrice >= 0"); builder.HasCheckConstraint("CK_OrderItems_DiscountAmount", "DiscountAmount >= 0"); builder.HasCheckConstraint("CK_OrderItems_TotalPrice", "TotalPrice >= 0"); } }`, // Data/Configurations/UserRoleConfiguration.cs 'Data/Configurations/UserRoleConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole> { public void Configure(EntityTypeBuilder<UserRole> builder) { builder.ToTable("UserRoles"); builder.HasKey(ur => ur.Id); builder.Property(ur => ur.Role) .IsRequired() .HasMaxLength(50); builder.Property(ur => ur.Description) .HasMaxLength(200); // Indexes builder.HasIndex(ur => new { ur.UserId, ur.Role }) .IsUnique() .HasDatabaseName("IX_UserRoles_UserId_Role"); // Relationships builder.HasOne(ur => ur.User) .WithMany(u => u.UserRoles) .HasForeignKey(ur => ur.UserId) .OnDelete(DeleteBehavior.Cascade); } }`, // Data/Configurations/ProductTagConfiguration.cs 'Data/Configurations/ProductTagConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class ProductTagConfiguration : IEntityTypeConfiguration<ProductTag> { public void Configure(EntityTypeBuilder<ProductTag> builder) { builder.ToTable("ProductTags"); builder.HasKey(pt => pt.Id); builder.Property(pt => pt.Tag) .IsRequired() .HasMaxLength(50); // Indexes builder.HasIndex(pt => new { pt.ProductId, pt.Tag }) .IsUnique() .HasDatabaseName("IX_ProductTags_ProductId_Tag"); builder.HasIndex(pt => pt.Tag) .HasDatabaseName("IX_ProductTags_Tag"); // Relationships builder.HasOne(pt => pt.Product) .WithMany(p => p.ProductTags) .HasForeignKey(pt => pt.ProductId) .OnDelete(DeleteBehavior.Cascade); } }`, // Data/Configurations/ProductImageConfiguration.cs 'Data/Configurations/ProductImageConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class ProductImageConfiguration : IEntityTypeConfiguration<ProductImage> { public void Configure(EntityTypeBuilder<ProductImage> builder) { builder.ToTable("ProductImages"); builder.HasKey(pi => pi.Id); builder.Property(pi => pi.ImageUrl) .IsRequired() .HasMaxLength(500); builder.Property(pi => pi.AltText) .HasMaxLength(200); // Indexes builder.HasIndex(pi => pi.ProductId) .HasDatabaseName("IX_ProductImages_ProductId"); builder.HasIndex(pi => pi.IsPrimary) .HasDatabaseName("IX_ProductImages_IsPrimary"); // Relationships builder.HasOne(pi => pi.Product) .WithMany(p => p.ProductImages) .HasForeignKey(pi => pi.ProductId) .OnDelete(DeleteBehavior.Cascade); } }`, // Data/Configurations/OrderStatusHistoryConfiguration.cs 'Data/Configurations/OrderStatusHistoryConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class OrderStatusHistoryConfiguration : IEntityTypeConfiguration<OrderStatusHistory> { public void Configure(EntityTypeBuilder<OrderStatusHistory> builder) { builder.ToTable("OrderStatusHistory"); builder.HasKey(osh => osh.Id); builder.Property(osh => osh.FromStatus) .IsRequired() .HasMaxLength(50); builder.Property(osh => osh.ToStatus) .IsRequired() .HasMaxLength(50); builder.Property(osh => osh.Notes) .HasMaxLength(500); builder.Property(osh => osh.ChangedBy) .HasMaxLength(100); // Indexes builder.HasIndex(osh => osh.OrderId) .HasDatabaseName("IX_OrderStatusHistory_OrderId"); builder.HasIndex(osh => osh.CreatedAt) .HasDatabaseName("IX_OrderStatusHistory_CreatedAt"); // Relationships builder.HasOne(osh => osh.Order) .WithMany(o => o.StatusHistory) .HasForeignKey(osh => osh.OrderId) .OnDelete(DeleteBehavior.Cascade); } }`, // Data/Configurations/AuditLogConfiguration.cs 'Data/Configurations/AuditLogConfiguration.cs': `using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using {{serviceName}}.Models; namespace {{serviceName}}.Data.Configurations; public class AuditLogConfiguration : IEntityTypeConfiguration<AuditLog> { public void Configure(EntityTypeBuilder<AuditLog> builder) { builder.ToTable("AuditLogs"); builder.HasKey(al => al.Id); builder.Property(al => al.EntityType) .IsRequired() .HasMaxLength(100); builder.Property(al => al.EntityId) .IsRequired(); builder.Property(al => al.Action) .IsRequired() .HasMaxLength(50); builder.Property(al => al.UserEmail) .HasMaxLength(100); builder.Property(al => al.IPAddress) .HasMaxLength(45); builder.Property(al => al.UserAgent) .HasMaxLength(500); builder.Property(al => al.Changes) .HasMaxLength(500); // Store JSON as text builder.Property(al => al.OldValues) .HasColumnType("nvarchar(max)"); builder.Property(al => al.NewValues) .HasColumnType("nvarchar(max)"); // Indexes builder.HasIndex(al => al.UserId) .HasDatabaseName("IX_AuditLogs_UserId"); builder.HasIndex(al => al.EntityType) .HasDatabaseName("IX_AuditLogs_EntityType"); builder.HasIndex(al => al.Action) .HasDatabaseName("IX_AuditLogs_Action"); builder.HasIndex(al => al.CreatedAt) .HasDatabaseName("IX_AuditLogs_CreatedAt"); // Relationships builder.HasOne(al => al.User) .WithMany(u => u.AuditLogs) .HasForeignKey(al => al.UserId) .OnDelete(DeleteBehavior.SetNull); } }`, // Infrastructure/IMigrationService.cs 'Infrastructure/IMigrationService.cs': `namespace {{serviceName}}.Infrastructure; public interface IMigrationService { Task ApplyMigrationsAsync(); Task<bool> HasPendingMigrationsAsync(); Task<IEnumerable<string>> GetPendingMigrationsAsync(); Task<IEnumerable<string>> GetAppliedMigrationsAsync(); Task ResetDatabaseAsync(); Task<bool> DatabaseExistsAsync(); Task CreateDatabaseAsync(); Task DropDatabaseAsync(); }`, // Infrastructure/MigrationService.cs 'Infrastructure/MigrationService.cs': `using Microsoft.EntityFrameworkCore; using {{serviceName}}.Data; namespace {{serviceName}}.Infrastructure; public class MigrationService : IMigrationService { private readonly ApplicationDbContext _context; private readonly ILogger<MigrationService> _logger; public MigrationService(ApplicationDbContext context, ILogger<MigrationService> logger) { _context = context; _logger = logger; } public async Task ApplyMigrationsAsync() { try { _logger.LogInformation("Checking for pending migrations..."); var pendingMigrations = await GetPendingMigrationsAsync(); if (pendingMigrations.Any()) { _logger.LogInformation("Found {Count} pending migrations. Applying...", pendingMigrations.Count()); foreach (var migration in pendingMigrations) { _logger.LogInformation("Pending migration: {Migration}", migration); } await _context.Database.MigrateAsync(); _logger.LogInformation("Migrations applied successfully"); } else { _logger.LogInformation("No pending migrations found"); } } catch (Exception ex) { _logger.LogError(ex, "Error applying migrations"); throw; } } public async Task<bool> HasPendingMigrationsAsync() { try { var pendingMigrations = await _context.Database.GetPendingMigrationsAsync(); return pendingMigrations.Any(); } catch (Exception ex) { _logger.LogError(ex, "Error checking for pending migrations"); throw; } } public async Task<IEnumerable<string>> GetPendingMigrationsAsync() { try { return await _context.Database.GetPendingMigrationsAsync(); } catch (Exception ex) { _logger.LogError(ex, "Error getting pending migrations"); throw; } } public async Task<IEnumerable<string>> GetAppliedMigrationsAsync() { try {