diff --git a/docs/en/CLI.md b/docs/en/CLI.md index 2e7b117799..373b07e3d7 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -106,6 +106,12 @@ abp new Acme.BookStore * `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D:\local-template` or `https://.../my-template-file.zip`). * `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder. * `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](Entity-Framework-Core-Other-DBMS.md) (after creating the solution). +* `--database-management-system` or `-dbms`: Sets the database management system. Default is **SQL Server**. `--connection-string` parameter should be set along with this parameter if you want to set any DBMS other than **SQL Server**. Supported DBMS's: + * `SqlServer` + * `MySQL` + * `SQLite` + * `Oracle-Devart` + * `PostgreSQL` * `--local-framework-ref --abp-path`: Uses local projects references to the ABP framework instead of using the NuGet packages. This can be useful if you download the ABP Framework source code and have a local reference to the framework from your application. * `--no-random-port`: Uses template's default ports. @@ -419,4 +425,4 @@ abp bundle [options] * ```--working-directory``` or ```-wd```: Specifies the working directory. This option is useful when executing directory doesn't contain a Blazor project file. * ```--force``` or ```-f```: Forces to build project before generating references. -`bundle` command reads the `appsettings.json` file inside the Blazor project for bundling options. For more details about managing style and script references in Blazor apps, see [Managing Global Scripts & Styles](UI/Blazor/Global-Scripts-Styles.md) \ No newline at end of file +`bundle` command reads the `appsettings.json` file inside the Blazor project for bundling options. For more details about managing style and script references in Blazor apps, see [Managing Global Scripts & Styles](UI/Blazor/Global-Scripts-Styles.md) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 75d2651cbc..94a690deb9 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; @@ -16,6 +17,7 @@ using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding.Building; using Volo.Abp.Cli.ProjectBuilding.Templates.App; using Volo.Abp.Cli.ProjectBuilding.Templates.Console; +using Volo.Abp.Cli.ProjectModification; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.Threading; @@ -24,14 +26,17 @@ namespace Volo.Abp.Cli.Commands { public class NewCommand : IConsoleCommand, ITransientDependency { + private readonly EfCoreMigrationRecreater _efCoreMigrationRecreater; public ILogger Logger { get; set; } protected TemplateProjectBuilder TemplateProjectBuilder { get; } public ITemplateInfoProvider TemplateInfoProvider { get; } public NewCommand(TemplateProjectBuilder templateProjectBuilder - , ITemplateInfoProvider templateInfoProvider) + , ITemplateInfoProvider templateInfoProvider, + EfCoreMigrationRecreater efCoreMigrationRecreater) { + _efCoreMigrationRecreater = efCoreMigrationRecreater; TemplateProjectBuilder = templateProjectBuilder; TemplateInfoProvider = templateInfoProvider; @@ -84,18 +89,31 @@ namespace Volo.Abp.Cli.Commands Logger.LogInformation("Database provider: " + databaseProvider); } - var uiFramework = GetUiFramework(commandLineArgs); - if (uiFramework != UiFramework.NotSpecified) - { - Logger.LogInformation("UI Framework: " + uiFramework); - } - var connectionString = GetConnectionString(commandLineArgs); if (connectionString != null) { Logger.LogInformation("Connection string: " + connectionString); } + var databaseManagementSystem = GetDatabaseManagementSystem(commandLineArgs); + if (databaseManagementSystem != DatabaseManagementSystem.NotSpecified) + { + Logger.LogInformation("DBMS: " + databaseManagementSystem); + } + + if (databaseManagementSystem != DatabaseManagementSystem.NotSpecified + && databaseManagementSystem != DatabaseManagementSystem.SQLServer + && connectionString == null) + { + throw new CliUsageException($"Connection string must be set if a Database Management System other than SQLServer is set. Use \"--{Options.ConnectionString.Long}\" parameter to set connection string"); + } + + var uiFramework = GetUiFramework(commandLineArgs); + if (uiFramework != UiFramework.NotSpecified) + { + Logger.LogInformation("UI Framework: " + uiFramework); + } + var mobileApp = GetMobilePreference(commandLineArgs); if (mobileApp != MobileApp.None) { @@ -147,6 +165,7 @@ namespace Volo.Abp.Cli.Commands template, version, databaseProvider, + databaseManagementSystem, uiFramework, mobileApp, gitHubAbpLocalRepositoryPath, @@ -196,6 +215,8 @@ namespace Volo.Abp.Cli.Commands } } + ReCreateMigrationsIfNeeded(databaseProvider, databaseManagementSystem, outputFolder); + Logger.LogInformation($"'{projectName}' has been successfully created to '{outputFolder}'"); if (AppTemplateBase.IsAppTemplate(template ?? (await TemplateInfoProvider.GetDefaultAsync()).Name)) @@ -205,6 +226,23 @@ namespace Volo.Abp.Cli.Commands } } + private void ReCreateMigrationsIfNeeded(DatabaseProvider databaseProvider, DatabaseManagementSystem databaseManagementSystem, string outputFolder) + { + if (databaseManagementSystem == DatabaseManagementSystem.NotSpecified || databaseManagementSystem == DatabaseManagementSystem.SQLServer) + { + return; + } + + if (databaseProvider != DatabaseProvider.NotSpecified && databaseProvider != DatabaseProvider.EntityFrameworkCore) + { + return; + } + + Logger.LogInformation($"Re-creating migrations... ({databaseManagementSystem})"); + + _efCoreMigrationRecreater.Recreate(outputFolder); + } + private void OpenThanksPage(UiFramework uiFramework, DatabaseProvider databaseProvider, bool tiered, bool commercial) { uiFramework = uiFramework == UiFramework.NotSpecified || uiFramework == UiFramework.None ? UiFramework.Mvc : uiFramework; @@ -267,6 +305,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine("-ts|--template-source (your local or network abp template source)"); sb.AppendLine("-csf|--create-solution-folder (default: true)"); sb.AppendLine("-cs|--connection-string (your database connection string)"); + sb.AppendLine("--dbms (your database management system. Requires --connection-string to be set)"); sb.AppendLine("--tiered (if supported by the template)"); sb.AppendLine("--no-ui (if supported by the template)"); sb.AppendLine("--no-random-port (Use template's default ports)"); @@ -289,6 +328,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine(" abp new Acme.BookStore -ts \"D:\\localTemplate\\abp\""); sb.AppendLine(" abp new Acme.BookStore -csf false"); sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\""); + sb.AppendLine(" abp new Acme.BookStore --dbms mysql --connection-string \"Server=myServerName\\myInstanceName;Database=myDatabase;User Id=myUsername;Password=myPassword\""); sb.AppendLine(" abp new Acme.BookStore --connection-string \"Server=myServerName\\myInstanceName;Database=myDatabase;User Id=myUsername;Password=myPassword\""); sb.AppendLine(""); sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); @@ -315,6 +355,34 @@ namespace Volo.Abp.Cli.Commands } } + protected virtual DatabaseManagementSystem GetDatabaseManagementSystem(CommandLineArgs commandLineArgs) + { + var optionValue = commandLineArgs.Options.GetOrNull(Options.DatabaseManagementSystem.Short, Options.DatabaseManagementSystem.Long); + + if (optionValue == null) + { + return DatabaseManagementSystem.NotSpecified; + } + + switch (optionValue.ToLowerInvariant()) + { + case "sqlserver": + return DatabaseManagementSystem.SQLServer; + case "mysql": + return DatabaseManagementSystem.MySQL; + case "postgresql": + return DatabaseManagementSystem.PostgreSQL; + case "oracle-devart": + return DatabaseManagementSystem.OracleDevart; + case "sqlite": + return DatabaseManagementSystem.SQLite; + case "oracle": // Currently disabled. See https://github.com/abpframework/abp/issues/6513 + // return DatabaseManagementSystem.Oracle; + default: + return DatabaseManagementSystem.NotSpecified; + } + } + protected virtual UiFramework GetUiFramework(CommandLineArgs commandLineArgs) { var optionValue = commandLineArgs.Options.GetOrNull(Options.UiFramework.Short, Options.UiFramework.Long); @@ -362,6 +430,12 @@ namespace Volo.Abp.Cli.Commands public const string Long = "database-provider"; } + public static class DatabaseManagementSystem + { + public const string Short = "dbms"; + public const string Long = "database-management-system"; + } + public static class OutputFolder { public const string Short = "o"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs index 78a95996bd..28f41e55d8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs @@ -37,6 +37,7 @@ namespace Volo.Abp.Cli.Commands.Services moduleName, version, DatabaseProvider.NotSpecified, + DatabaseManagementSystem.NotSpecified, UiFramework.NotSpecified, null, gitHubAbpLocalRepositoryPath, diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs new file mode 100644 index 0000000000..fcb3130854 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseManagementSystem.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public enum DatabaseManagementSystem + { + NotSpecified, + SQLServer, + MySQL, + PostgreSQL, + Oracle, + OracleDevart, + SQLite + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs new file mode 100644 index 0000000000..4286710899 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/DatabaseManagementSystemChangeStep.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class DatabaseManagementSystemChangeStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + switch (context.BuildArgs.DatabaseManagementSystem) + { + case DatabaseManagementSystem.MySQL: + ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.MySQL", + "Volo.Abp.EntityFrameworkCore.MySQL", + "AbpEntityFrameworkCoreMySQLModule"); + AddMySqlServerVersion(context); + ChangeUseSqlServer(context,"UseMySQL", "UseMySql"); + break; + + case DatabaseManagementSystem.PostgreSQL: + ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.PostgreSql", + "Volo.Abp.EntityFrameworkCore.PostgreSql", + "AbpEntityFrameworkCorePostgreSqlModule"); + ChangeUseSqlServer(context,"UseNpgsql"); + break; + + case DatabaseManagementSystem.Oracle: + ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.Oracle", + "Volo.Abp.EntityFrameworkCore.Oracle", + "AbpEntityFrameworkCoreOracleModule"); + ChangeUseSqlServer(context,"UseOracle"); + break; + + case DatabaseManagementSystem.OracleDevart: + ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.Oracle.Devart", + "Volo.Abp.EntityFrameworkCore.Oracle.Devart", + "AbpEntityFrameworkCoreOracleDevartModule"); + AdjustOracleDbContextOptionsBuilder(context); + ChangeUseSqlServer(context,"UseOracle"); + break; + + case DatabaseManagementSystem.SQLite: + ChangeEntityFrameworkCoreDependency(context,"Volo.Abp.EntityFrameworkCore.Sqlite", + "Volo.Abp.EntityFrameworkCore.Sqlite", + "AbpEntityFrameworkCoreSqliteModule"); + ChangeUseSqlServer(context,"UseSqlite"); + break; + + default: + return; + } + } + + private void AdjustOracleDbContextOptionsBuilder(ProjectBuildContext context) + { + var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); + + dbContextFactoryFile.ReplaceText("new DbContextOptionsBuilder", + $"(DbContextOptionsBuilder<{context.BuildArgs.SolutionName.ProjectName}MigrationsDbContext>) new DbContextOptionsBuilder"); + } + + private void AddMySqlServerVersion(ProjectBuildContext context) + { + var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); + + dbContextFactoryFile.ReplaceText("configuration.GetConnectionString(\"Default\")", + "configuration.GetConnectionString(\"Default\"), MySqlServerVersion.LatestSupportedServerVersion"); + } + + private void ChangeEntityFrameworkCoreDependency(ProjectBuildContext context, string newPackageName, string newModuleNamespace, string newModuleClass) + { + var efCoreProjectFile = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCore.csproj", StringComparison.OrdinalIgnoreCase)); + efCoreProjectFile.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newPackageName); + + var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase)); + efCoreModuleClass.ReplaceText("Volo.Abp.EntityFrameworkCore.SqlServer", newModuleNamespace); + efCoreModuleClass.ReplaceText("AbpEntityFrameworkCoreSqlServerModule", newModuleClass); + } + + private void ChangeUseSqlServer(ProjectBuildContext context, string newUseMethodForEfModule, string newUseMethodForDbContext = null) + { + if (newUseMethodForDbContext == null) + { + newUseMethodForDbContext = newUseMethodForEfModule; + } + + var oldUseMethod = "UseSqlServer"; + + var efCoreModuleClass = context.Files.First(f => f.Name.EndsWith("EntityFrameworkCoreModule.cs", StringComparison.OrdinalIgnoreCase)); + efCoreModuleClass.ReplaceText(oldUseMethod, newUseMethodForEfModule); + + var dbContextFactoryFile = context.Files.First(f => f.Name.EndsWith("MigrationsDbContextFactory.cs", StringComparison.OrdinalIgnoreCase)); + dbContextFactoryFile.ReplaceText(oldUseMethod, newUseMethodForDbContext); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs index ad5da58a8e..9590001ea4 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs @@ -25,6 +25,12 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building pipeline.Steps.Add(new LicenseCodeReplaceStep()); } + if (context.Template.Name == AppTemplate.TemplateName || + context.Template.Name == AppProTemplate.TemplateName) + { + pipeline.Steps.Add(new DatabaseManagementSystemChangeStep()); + } + if ((context.BuildArgs.UiFramework == UiFramework.Mvc || context.BuildArgs.UiFramework == UiFramework.Blazor) && context.BuildArgs.MobileApp == MobileApp.None) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs index 52fa76e551..1022f5f10e 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs @@ -17,6 +17,8 @@ namespace Volo.Abp.Cli.ProjectBuilding public DatabaseProvider DatabaseProvider { get; set; } + public DatabaseManagementSystem DatabaseManagementSystem { get; set; } + public UiFramework UiFramework { get; set; } public MobileApp? MobileApp { get; set; } @@ -41,6 +43,7 @@ namespace Volo.Abp.Cli.ProjectBuilding [CanBeNull] string templateName = null, [CanBeNull] string version = null, DatabaseProvider databaseProvider = DatabaseProvider.NotSpecified, + DatabaseManagementSystem databaseManagementSystem = DatabaseManagementSystem.NotSpecified, UiFramework uiFramework = UiFramework.NotSpecified, MobileApp? mobileApp = null, [CanBeNull] string abpGitHubLocalRepositoryPath = null, @@ -53,6 +56,7 @@ namespace Volo.Abp.Cli.ProjectBuilding TemplateName = templateName; Version = version; DatabaseProvider = databaseProvider; + DatabaseManagementSystem = databaseManagementSystem; UiFramework = uiFramework; MobileApp = mobileApp; AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath; @@ -62,4 +66,4 @@ namespace Volo.Abp.Cli.ProjectBuilding ConnectionString = connectionString; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationRecreater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationRecreater.cs new file mode 100644 index 0000000000..3abdaf9392 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationRecreater.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class EfCoreMigrationRecreater : ITransientDependency + { + public ILogger Logger { get; set; } + + public EfCoreMigrationRecreater() + { + Logger = NullLogger.Instance; + } + + public void Recreate(string solutionFolder) + { + if (Directory.Exists(Path.Combine(solutionFolder, "aspnet-core"))) + { + solutionFolder = Path.Combine(solutionFolder, "aspnet-core"); + } + + var srcFolder = Path.Combine(solutionFolder, "src"); + + try + { + var migrationsFolder = Directory.GetDirectories(srcFolder).First(d => d.EndsWith(".EntityFrameworkCore.DbMigrations")); + Directory.Delete(Path.Combine(migrationsFolder, "Migrations"), true); + + var migratorFolder = Directory.GetDirectories(srcFolder).First(d => d.EndsWith(".DbMigrator")); + var migratorProjectFile = Directory.GetFiles(migratorFolder).First(d => d.EndsWith(".DbMigrator.csproj")); + var addMigrationCommand = $"dotnet ef migrations add Initial --startup-project {migratorProjectFile}"; + CmdHelper.RunCmd($"cd {migrationsFolder} && {addMigrationCommand}"); + } + catch (Exception e) + { + Logger.LogWarning("Re-creating migrations process failed."); + throw e; + } + } + } +}