@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
JavaScript
"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
{