public override async Task <int> Main() { try { Environment.CurrentDirectory = PathManager.FindResource(); } catch (FileNotFoundException ex) { Console.WriteLine(ex.Message); Console.WriteLine("Use `nfpm scaffold` to generate a NFive plugin in this directory"); return(1); } AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { if (args.Name.Contains(".resources")) { return(null); } var fileName = args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.InvariantCultureIgnoreCase)) + ".dll"; if (File.Exists(fileName)) { return(Assembly.Load(File.ReadAllBytes(fileName))); } var path = Directory.EnumerateFiles("plugins", "*.dll", SearchOption.AllDirectories).FirstOrDefault(f => Path.GetFileName(f) == fileName); if (string.IsNullOrEmpty(path)) { throw new FileLoadException(args.Name); } return(Assembly.Load(File.ReadAllBytes(path))); }; DTE dte = null; try { if (!File.Exists(this.Sln)) { this.Sln = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.sln", SearchOption.TopDirectoryOnly).FirstOrDefault(); } if (this.Sln == null || !File.Exists(this.Sln)) { this.Sln = Input.String("Visual Studio SLN solution file"); } Console.Write("Searching for existing Visual Studio instance..."); dte = VisualStudio.GetInstances().FirstOrDefault(env => env.Solution.FileName == this.Sln); if (dte != default) { Console.WriteLine(" found"); } else { Console.WriteLine(" not found"); Console.WriteLine("Starting new Visual Studio instance..."); dte = (DTE)Activator.CreateInstance(Type.GetTypeFromProgID("VisualStudio.DTE", true), true); this.existingInstance = false; } Console.WriteLine("Opening solution"); var solution = Retry.Do(() => dte.Solution); if (!Retry.Do(() => solution.IsOpen)) { Retry.Do(() => solution.Open(this.Sln)); } Console.WriteLine("Building solution"); solution.SolutionBuild.Build(true); Console.WriteLine("Searching for projects"); var pp = Retry.Do(() => solution.Projects.Cast <Project>().ToList()); var ppp = Retry.Do(() => pp.Where(p => !string.IsNullOrWhiteSpace(p.FullName)).ToList()); foreach (var project in ppp) { Console.WriteLine($" Analyzing project {Retry.Do(() => project.Name)}..."); var projectPath = Path.GetDirectoryName(Retry.Do(() => project.FullName)) ?? string.Empty; var outputPath = Path.GetFullPath(Path.Combine(projectPath, Retry.Do(() => project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString()), Retry.Do(() => project.Properties.Item("OutputFileName").Value.ToString()))); var asm = Assembly.Load(File.ReadAllBytes(outputPath)); if (!this.Sdk && asm.GetCustomAttribute <ServerPluginAttribute>() == null) { continue; } var contextType = asm.DefinedTypes.FirstOrDefault(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(EFContext <>)); if (contextType == default) { continue; } Console.WriteLine($" Loaded {outputPath}"); Console.WriteLine($" Found DB context: {contextType.Name}"); var props = contextType .GetProperties() .Where(p => p.CanRead && p.CanWrite && p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet <>) && p.PropertyType.GenericTypeArguments.Any(t => !string.IsNullOrEmpty(t.Namespace) && t.Namespace.StartsWith("NFive.SDK."))) // TODO .Select(t => $"dbo.{t.Name}") // TODO .ToArray(); if (!this.Sdk) { Console.WriteLine($" Excluding tables: {string.Join(", ", props)}"); } var migrationsPath = "Migrations"; if (!Directory.Exists(Path.Combine(projectPath, migrationsPath))) { migrationsPath = Input.String("Migration source code folder", "Migrations"); // TODO: Validate } var @namespace = $"{project.Properties.Item("RootNamespace").Value}.{migrationsPath}"; if (asm.DefinedTypes.Any(t => t.BaseType != null && t.BaseType == typeof(DbMigration) && t.Namespace == @namespace && t.Name == this.Name)) { throw new Exception($"A migration named \"{this.Name}\" already exists at \"{@namespace}.{this.Name}\", please use another migration name."); } Console.WriteLine(" Generating migration..."); var migrationsConfiguration = new DbMigrationsConfiguration { AutomaticMigrationDataLossAllowed = false, AutomaticMigrationsEnabled = false, CodeGenerator = new NFiveMigrationCodeGenerator(this.Sdk ? new string[] { } : props), ContextType = contextType, ContextKey = $"{@namespace}.Configuration", MigrationsAssembly = asm, MigrationsDirectory = migrationsPath, MigrationsNamespace = @namespace, TargetDatabase = new DbConnectionInfo(this.Database, "MySql.Data.MySqlClient") }; var ms = new MigrationScaffolder(migrationsConfiguration); if (this.RunMigrations) { var migrator = new DbMigrator(migrationsConfiguration); if (migrator.GetPendingMigrations().Any()) { Console.WriteLine(" Running existing migrations..."); foreach (var migration in migrator.GetPendingMigrations()) { Console.WriteLine($" Running migration: {migration}"); migrator.Update(migration); } } } Console.WriteLine(" Scaffolding migration..."); var src = ms.Scaffold(this.Name, false); var file = Path.Combine(projectPath, migrationsPath, $"{src.MigrationId}.{src.Language}"); Console.WriteLine($" Writing migration: {file}"); File.WriteAllText(file, src.UserCode); Console.WriteLine(" Updating project..."); project.ProjectItems.AddFromFile(file); project.Save(); } Console.WriteLine("Building solution..."); solution.SolutionBuild.Build(true); if (!this.existingInstance) { Console.WriteLine("Quitting Visual Studio instance"); dte.Quit(); } Console.WriteLine("Done"); return(await Task.FromResult(0)); } catch (ReflectionTypeLoadException ex) { Console.WriteLine(ex.Message); Console.WriteLine(string.Join(Environment.NewLine, ex.LoaderExceptions.Select(e => e.Message))); return(1); } catch (Exception ex) { Console.WriteLine(ex.Message); return(1); } finally { if (!this.existingInstance) { dte.Quit(); } } }