Browse Source

Merge pull request #1126 from abpframework/cli-onfigure-Entity-Framework-after-installing-a-module

Cli onfigure entity framework after installing a module
pull/1136/head
Yunus Emre Kalkan 7 years ago
committed by GitHub
parent
commit
d47f023d51
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs
  2. 84
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs
  3. 55
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs
  4. 47
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs
  5. 44
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleClassDependcyAdder.cs
  6. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs
  7. 48
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  8. 63
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/UsingStatementAdder.cs

13
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.DbMigrations.Skip) ?? "false");
await SolutionModuleAdder.AddAsync(
GetSolutionFile(commandLineArgs),
commandLineArgs.Target
commandLineArgs.Target,
skipDbMigrations
);
}
@ -89,10 +93,12 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine("");
sb.AppendLine("Options:");
sb.AppendLine(" -s|--solution <solution-file> Specify the solution file explicitly.");
sb.AppendLine(" --skip-db-migrations <boolean> Specify if a new migration will be added or not.");
sb.AppendLine("");
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 --skip-db-migrations false Adds the module to the given soluton but doesn't add-migration.");
sb.AppendLine("");
return sb.ToString();
@ -105,6 +111,11 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "s";
public const string Long = "solution";
}
public static class DbMigrations
{
public const string Skip = "skip-db-migrations";
}
}
}
}

84
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs

@ -0,0 +1,84 @@
using System;
using System.IO;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectModification
{
public class DbContextFileBuilderConfigureAdder : ITransientDependency
{
public ILogger<DbContextFileBuilderConfigureAdder> Logger { get; set; }
protected UsingStatementAdder UsingStatementAdder { get; }
public DbContextFileBuilderConfigureAdder(UsingStatementAdder usingStatementAdder)
{
UsingStatementAdder = usingStatementAdder;
Logger = NullLogger<DbContextFileBuilderConfigureAdder>.Instance;
}
public void Add(string path, string moduleConfiguration)
{
var file = File.ReadAllText(path);
file = UsingStatementAdder.Add(file, GetNamespace(moduleConfiguration));
var stringToAdd = GetLineToAdd(moduleConfiguration);
if (!file.Contains(stringToAdd))
{
var indexToInsert = FindIndexToInsert(file);
if (indexToInsert <= 0 || indexToInsert >= file.Length)
{
Logger.LogWarning($"\"OnModelCreating(ModelBuilder builder)\" method couldn't be found in {path}");
return;
}
file = file.Insert(indexToInsert, " " + stringToAdd + Environment.NewLine + " ");
}
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++;
if (index >= file.Length)
{
break;
}
if (file[index] == '{')
{
stack++;
}
else if (file[index] == '}')
{
stack--;
}
}
return index;
}
protected string GetLineToAdd(string moduleConfiguration)
{
return "builder." + moduleConfiguration.Split(':')[1] + "();";
}
protected string GetNamespace(string moduleConfiguration)
{
return string.Join(".", moduleConfiguration.Split(':')[0]);
}
}
}

55
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs

@ -1,42 +1,83 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectModification
{
public class DerivedClassFinder : ITransientDependency
{
public ILogger<DerivedClassFinder> Logger { get; set; }
public DerivedClassFinder()
{
Logger = NullLogger<DerivedClassFinder>.Instance;
}
public virtual List<string> Find(string csprojFilePath, string baseClass)
{
var moduleFilePaths = new List<string>();
var csprojFileDirectory = Path.GetDirectoryName(csprojFilePath);
var binFile = Path.Combine(csprojFileDirectory, "bin");
var objFile = Path.Combine(csprojFileDirectory, "obj");
var csFiles = new DirectoryInfo(Path.GetDirectoryName(csprojFilePath))
var csFiles = new DirectoryInfo(csprojFileDirectory)
.GetFiles("*.cs", SearchOption.AllDirectories)
.Where(f => !f.DirectoryName.StartsWith(binFile))
.Where(f => !f.DirectoryName.StartsWith(objFile))
.Select(f => f.FullName)
.ToList();
foreach (var csFile in csFiles)
{
if (IsDerivedFromAbpModule(csFile, baseClass))
try
{
moduleFilePaths.Add(csFile);
if (IsDerived(csFile, baseClass))
{
moduleFilePaths.Add(csFile);
}
}
catch (Exception)
{
Logger.LogDebug($"Couldn't parse {csFile}.");
}
}
return moduleFilePaths;
}
protected bool IsDerivedFromAbpModule(string csFile, string baseClass)
protected bool IsDerived(string csFile, string baseClass)
{
Logger.LogDebug(csFile);
var root = CSharpSyntaxTree.ParseText(File.ReadAllText(csFile)).GetRoot();
var namespaceSyntax = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().First();
var classDeclaration = (namespaceSyntax.DescendantNodes().OfType<ClassDeclarationSyntax>()).First();
var baseTypeList = classDeclaration.BaseList?.Types.Select(t => t.ToString()).ToList();
if (baseTypeList == null)
{
return false;
}
foreach (var baseType in baseTypeList)
{
if (baseType.Contains('<') && baseType.Substring(0, baseType.IndexOf('<')) == baseClass)
{
return true;
}
if (baseType == baseClass)
{
return true;
}
}
return classDeclaration.BaseList?.Types
.Any(t => t.ToString().Equals(baseClass)) ?? false;
return false;
}
}
}

47
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs

@ -0,0 +1,47 @@
using System;
using System.Diagnostics;
using System.IO;
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 \"" + Path.GetDirectoryName(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 \"" + Path.GetDirectoryName(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);
}
}
}

44
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleClassDependcyAdder.cs

@ -7,16 +7,20 @@ namespace Volo.Abp.Cli.ProjectModification
{
public class ModuleClassDependcyAdder : ITransientDependency
{
protected UsingStatementAdder UsingStatementAdder { get; }
public ModuleClassDependcyAdder(UsingStatementAdder usingStatementAdder)
{
UsingStatementAdder = usingStatementAdder;
}
public virtual void Add(string path, string module)
{
ParseModuleNameAndNameSpace(module, out var nameSpace, out var moduleName);
var file = File.ReadAllText(path);
if (!file.Contains(GetUsingStatement(nameSpace)) )
{
file = InsertUsingStatement(file, nameSpace);
}
file = UsingStatementAdder.Add(file, nameSpace);
if (!file.Contains(moduleName) )
{
@ -34,38 +38,6 @@ namespace Volo.Abp.Cli.ProjectModification
return file.Insert(indexOfPublicClassDeclaration, dependsOnAttribute);
}
protected virtual string InsertUsingStatement(string file, string nameSpace)
{
var indexOfTheEndOfTheLastUsingStatement = GetIndexOfTheEndOfTheLastUsingStatement(file);
return file.Insert(indexOfTheEndOfTheLastUsingStatement, Environment.NewLine + GetUsingStatement(nameSpace));
}
protected virtual int GetIndexOfTheEndOfTheLastUsingStatement(string file)
{
var indexOfPublicClassDeclaration = GetIndexOfWhereDependsOnWillBeAdded(file);
file = file.Substring(0, indexOfPublicClassDeclaration);
var indexOfTheStartOfLastUsingStatement =
file.LastIndexOf("using ", StringComparison.Ordinal);
if (indexOfTheStartOfLastUsingStatement < 0)
{
return 0;
}
var indexOfFirstSemiColonAfterLastUsingStatement =
file.Substring(indexOfTheStartOfLastUsingStatement).IndexOf(';');
if (indexOfFirstSemiColonAfterLastUsingStatement < 0)
{
return 0;
}
return indexOfTheStartOfLastUsingStatement
+ indexOfFirstSemiColonAfterLastUsingStatement + 1;
}
protected virtual int GetIndexOfWhereDependsOnWillBeAdded(string file)
{
var indexOfPublicClassDeclaration = file.IndexOf("public class", StringComparison.Ordinal);

2
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<NugetPackageInfo> NugetPackages { get; set; }
public List<NpmPackageInfo> NpmPackages { get; set; }

48
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<SolutionModuleAdder>.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<ModuleInfo> FindModuleInfoAsync(string moduleName)
{
using (var client = new HttpClient())

63
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/UsingStatementAdder.cs

@ -0,0 +1,63 @@
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectModification
{
public class UsingStatementAdder : ITransientDependency
{
public string Add(string fileContent, string nameSpace)
{
if (fileContent.Contains(nameSpace))
{
return fileContent;
}
var index = GetIndexOfTheEndOfTheLastUsingStatement(fileContent);
if (index < 0 || index >= fileContent.Length)
{
index = 0;
}
var usingStatement = GetUsingStatement(nameSpace);
return fileContent.Insert(index, usingStatement);
}
protected string GetUsingStatement(string nameSpace)
{
return Environment.NewLine + "using " + nameSpace + ";";
}
protected virtual int GetIndexOfTheEndOfTheLastUsingStatement(string fileContent)
{
var indexOfNamespaceDeclaration = fileContent.IndexOf("namespace", StringComparison.Ordinal);
if (indexOfNamespaceDeclaration < 0)
{
return 0;
}
fileContent = fileContent.Substring(0, indexOfNamespaceDeclaration);
var indexOfTheStartOfLastUsingStatement =
fileContent.LastIndexOf("using ", StringComparison.Ordinal);
if (indexOfTheStartOfLastUsingStatement < 0)
{
return 0;
}
var indexOfFirstSemiColonAfterLastUsingStatement =
fileContent.Substring(indexOfTheStartOfLastUsingStatement).IndexOf(';');
if (indexOfFirstSemiColonAfterLastUsingStatement < 0)
{
return 0;
}
return indexOfTheStartOfLastUsingStatement
+ indexOfFirstSemiColonAfterLastUsingStatement + 1;
}
}
}
Loading…
Cancel
Save