diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs index 5b07f7f364..f48e2f4542 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs @@ -30,9 +30,13 @@ namespace Volo.Abp.Cli.Commands throw new CliUsageException("Module name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); } + var skipDbMigrations = Convert.ToBoolean( + commandLineArgs.Options.GetOrNull(Options.Solution.SkipDbMigrations) ?? "false"); + await SolutionModuleAdder.AddAsync( GetSolutionFile(commandLineArgs), - commandLineArgs.Target + commandLineArgs.Target, + skipDbMigrations ); } @@ -93,6 +97,7 @@ namespace Volo.Abp.Cli.Commands sb.AppendLine("Examples:"); sb.AppendLine(" abp add-module Volo.Blogging Adds the module to the current soluton."); sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore Adds the module to the given soluton."); + sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore --SkipDbMigrations false Adds the module to the given soluton but doesn't add-migration."); sb.AppendLine(""); return sb.ToString(); @@ -104,6 +109,7 @@ namespace Volo.Abp.Cli.Commands { public const string Short = "s"; public const string Long = "solution"; + public const string SkipDbMigrations = "skip-db-migrations"; } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs new file mode 100644 index 0000000000..8f4d48adfa --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class DbContextFileBuilderConfigureAdder : ITransientDependency + { + public void Add(string path, string moduleConfiguration) + { + var file = File.ReadAllText(path); + + var indexToInsert = FindIndexToInsert(file); + var stringToAdd = GetLineToAdd(moduleConfiguration); + + stringToAdd = " " + stringToAdd + Environment.NewLine; + + file = file.Insert(indexToInsert, stringToAdd); + + File.WriteAllText(path, file); + } + + protected int FindIndexToInsert(string file) + { + var indexOfMethodDeclaration = file.IndexOf("OnModelCreating(", StringComparison.Ordinal); + var indexOfOpeningBracket = indexOfMethodDeclaration + file.Substring(indexOfMethodDeclaration).IndexOf('{'); + + var stack = 1; + var index = indexOfOpeningBracket; + + while (stack > 0 || index < file.Length) + { + index++; + if (file[index] == '{') + { + stack++; + } + else if (file[index] == '}') + { + stack--; + } + } + return index; + } + + protected string GetLineToAdd(string moduleConfiguration) + { + return "builder.Configure" + moduleConfiguration + "();"; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs new file mode 100644 index 0000000000..f22e6107b4 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class EfCoreMigrationAdder : ITransientDependency + { + public void AddMigration(string csprojFile, string module, bool updateDatabase = true) + { + var moduleName = ParseModuleName(module); + var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix(); + + var process = Process.Start("CMD.exe", "/C cd \"" + csprojFile + "\" & dotnet ef migrations add " + migrationName); + process.WaitForExit(); + + if (updateDatabase) + { + UpdateDatabase(csprojFile); + } + } + + protected void UpdateDatabase(string csprojFile) + { + var process = Process.Start("CMD.exe", "/C cd \"" + csprojFile + "\" & dotnet ef database update"); + process.WaitForExit(); + } + + protected virtual string ParseModuleName(string fullModuleName) + { + var words = fullModuleName?.Split('.'); + + if (words == null || words.Length <= 1) + { + return ""; + } + + return words[words.Length - 1]; + } + + protected virtual string GetUniquePostFix() + { + return "_" + new Random().Next(1,99999); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs index 08c70eeede..d5e7a4e15a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs @@ -8,6 +8,8 @@ namespace Volo.Abp.Cli.ProjectModification public string DisplayName { get; set; } + public string EfCoreConfigureMethodName { get; set; } + public List NugetPackages { get; set; } public List NpmPackages { get; set; } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index 917a7bf1a0..6048338b10 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -19,22 +19,32 @@ namespace Volo.Abp.Cli.ProjectModification protected IJsonSerializer JsonSerializer { get; } protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; } + protected DbContextFileBuilderConfigureAdder DbContextFileBuilderConfigureAdder { get; } + protected EfCoreMigrationAdder EfCoreMigrationAdder { get; } protected ProjectNpmPackageAdder ProjectNpmPackageAdder { get; } + protected DerivedClassFinder DerivedClassFinder { get; } public SolutionModuleAdder( IJsonSerializer jsonSerializer, ProjectNugetPackageAdder projectNugetPackageAdder, + DbContextFileBuilderConfigureAdder dbContextFileBuilderConfigureAdder, + EfCoreMigrationAdder efCoreMigrationAdder, + DerivedClassFinder derivedClassFinder, ProjectNpmPackageAdder projectNpmPackageAdder) { + EfCoreMigrationAdder = efCoreMigrationAdder; + DerivedClassFinder = derivedClassFinder; JsonSerializer = jsonSerializer; ProjectNugetPackageAdder = projectNugetPackageAdder; + DbContextFileBuilderConfigureAdder = dbContextFileBuilderConfigureAdder; ProjectNpmPackageAdder = projectNpmPackageAdder; Logger = NullLogger.Instance; } public virtual async Task AddAsync( [NotNull] string solutionFile, - [NotNull] string moduleName) + [NotNull] string moduleName, + bool skipDbMigrations = false) { Check.NotNull(solutionFile, nameof(solutionFile)); Check.NotNull(moduleName, nameof(moduleName)); @@ -75,8 +85,42 @@ namespace Volo.Abp.Cli.ProjectModification Logger.LogDebug("Target project is not available for NPM packages."); } } + + ModifyDbContext(projectFiles, module, skipDbMigrations); + } + + protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, bool skipDbMigrations = false) + { + if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName)) + { + return; + } + + var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj")); + + if (dbMigrationsProject == null) + { + Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project."); + return; + } + + var dbContextFile = DerivedClassFinder.Find(dbMigrationsProject, "AbpDbContext").FirstOrDefault(); + + if (dbContextFile == null) + { + Logger.LogDebug($"{dbMigrationsProject} project doesn't have a class that is derived from \"AbpDbContext\"."); + return; + } + + DbContextFileBuilderConfigureAdder.Add(dbContextFile, module.EfCoreConfigureMethodName); + + + if (!skipDbMigrations) + { + EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name); + } } - + protected virtual async Task FindModuleInfoAsync(string moduleName) { using (var client = new HttpClient())