using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using SmartStack.Infrastructure.Persistence; using SmartStack.Infrastructure.Persistence.Configurations; namespace {{ProjectName}}.Api.Persistence; /// /// Factory for creating the SmartStack CoreDbContext at design time (for EF Core CLI tools). /// /// The concrete CoreDbContext type lives in the SmartStack NuGet package. EF's design-time /// discovery only scans the startup assembly (this project) and the --project assembly for /// context types and factories — never referenced packages — so without this factory, /// `dotnet ef` (and the deploy pipeline's migration-bundle build) can only see /// ExtensionsDbContext and the Core migrations never ship in a bundle. /// /// It MUST live in the STARTUP assembly (the Api): EF's initial factory scan only covers /// the startup assembly, and that scan is the only discovery path for a context type /// defined in a referenced package. /// /// This enables running Core migrations from the command line / CI: /// /// dotnet ef migrations bundle --context CoreDbContext --startup-project src/{{ProjectName}}.Api /// dotnet ef migrations list --context CoreDbContext --startup-project src/{{ProjectName}}.Api /// /// /// IMPORTANT: Core migrations come from the SmartStack package assembly and live in the /// 'core' schema, separate from client migrations in the 'extensions' schema. Apply Core /// BEFORE Extensions — extensions.* tables carry FKs into core.* tables. /// public class DesignTimeCoreDbContextFactory : IDesignTimeDbContextFactory { public CoreDbContext CreateDbContext(string[] args) { // Try to find appsettings.json in various locations var basePath = Directory.GetCurrentDirectory(); // Look for appsettings.json in common locations var configPaths = new[] { basePath, Path.Combine(basePath, "..", "{{ProjectName}}.Api"), Path.Combine(basePath, "..", "..", "src", "{{ProjectName}}.Api"), }; IConfigurationRoot? configuration = null; foreach (var path in configPaths) { var configPath = Path.Combine(path, "appsettings.json"); if (File.Exists(configPath)) { configuration = new ConfigurationBuilder() .SetBasePath(path) .AddJsonFile("appsettings.json", optional: false) .AddJsonFile("appsettings.Development.json", optional: true) .AddJsonFile("appsettings.Local.json", optional: true) .Build(); break; } } // Fallback: use a default connection string for design-time operations var connectionString = configuration?.GetConnectionString("DefaultConnection") ?? "Server=(localdb)\\mssqllocaldb;Database={{ProjectName}}_DesignTime;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"; var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlServer(connectionString, b => { // Core migrations are compiled into the SmartStack package assembly b.MigrationsAssembly(typeof(CoreDbContext).Assembly.FullName); // IMPORTANT: Core migrations history lives in the 'core' schema b.MigrationsHistoryTable("__EFMigrationsHistory", SchemaConstants.Core); }); return new CoreDbContext(optionsBuilder.Options); } }