From 33f45d504b1b3d4f78a7d45b9a994c198c91a744 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 22 Feb 2021 14:06:56 +0300 Subject: [PATCH 01/10] CLI CreateMigrationAndRunMigrator: check if EF Core global tool installed resolves https://github.com/abpframework/abp/issues/7800 --- .../Commands/CreateMigrationAndRunMigrator.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs index 982ef77065..b6eadd4183 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs @@ -2,6 +2,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; @@ -10,6 +12,13 @@ namespace Volo.Abp.Cli.Commands { public class CreateMigrationAndRunMigrator : IConsoleCommand, ITransientDependency { + public ILogger Logger { get; set; } + + public CreateMigrationAndRunMigrator() + { + Logger = NullLogger.Instance; + } + public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs) { if (commandLineArgs.Target.IsNullOrEmpty()) @@ -26,6 +35,8 @@ namespace Volo.Abp.Cli.Commands throw new Exception("DbMigrator is not found!"); } + await CheckAndInstallDotnetEfIfNeededAsync(); + var output = CmdHelper.RunCmdAndGetOutput($"cd \"{commandLineArgs.Target}\" && dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\""); if (output.Contains("Done.") && output.Contains("To undo this action") && output.Contains("ef migrations remove")) // Migration added successfully @@ -38,6 +49,22 @@ namespace Volo.Abp.Cli.Commands } } + private async Task CheckAndInstallDotnetEfIfNeededAsync() + { + var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g"); + + if (output.Contains("dotnet-ef")) + { + return; + } + + Logger.LogInformation("Installing dotnet-ef tool..."); + + CmdHelper.RunCmd("dotnet tool install --global dotnet-ef"); + + Logger.LogInformation("dotnet-ef tool is installed."); + } + private string GetDbMigratorProjectPath(string dbMigrationsFolderPath) { var srcFolder = Directory.GetParent(dbMigrationsFolderPath); From 37973efb459ec0615cad14b36e94168635b95a02 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 22 Feb 2021 14:07:43 +0300 Subject: [PATCH 02/10] rename CreateMigrationAndRunMigrator to CreateMigrationAndRunMigratorCommand --- .../Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs | 2 +- ...igrator.cs => CreateMigrationAndRunMigratorCommand.cs} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/{CreateMigrationAndRunMigrator.cs => CreateMigrationAndRunMigratorCommand.cs} (89%) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs index 9208357d4a..7ee6700d43 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs @@ -40,7 +40,7 @@ namespace Volo.Abp.Cli options.Commands["translate"] = typeof(TranslateCommand); options.Commands["build"] = typeof(BuildCommand); options.Commands["bundle"] = typeof(BundleCommand); - options.Commands["create-migration-and-run-migrator"] = typeof(CreateMigrationAndRunMigrator); + options.Commands["create-migration-and-run-migrator"] = typeof(CreateMigrationAndRunMigratorCommand); }); } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs similarity index 89% rename from framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs rename to framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs index b6eadd4183..3946ca35bb 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigrator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs @@ -10,13 +10,13 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Cli.Commands { - public class CreateMigrationAndRunMigrator : IConsoleCommand, ITransientDependency + public class CreateMigrationAndRunMigratorCommand : IConsoleCommand, ITransientDependency { - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } - public CreateMigrationAndRunMigrator() + public CreateMigrationAndRunMigratorCommand() { - Logger = NullLogger.Instance; + Logger = NullLogger.Instance; } public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs) From 9a81666557e07162043b6854b3195e1557d51672 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Tue, 23 Feb 2021 10:10:50 +0300 Subject: [PATCH 03/10] refactor CreateMigrationAndRunMigratorCommand.cs --- .../CreateMigrationAndRunMigratorCommand.cs | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs index 3946ca35bb..68fae779ce 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs @@ -23,60 +23,66 @@ namespace Volo.Abp.Cli.Commands { if (commandLineArgs.Target.IsNullOrEmpty()) { - throw new CliUsageException( - "DbMigrations folder path is missing!" - ); + throw new CliUsageException("DbMigrations folder path is missing!"); } var dbMigratorProjectPath = GetDbMigratorProjectPath(commandLineArgs.Target); - if (dbMigratorProjectPath == null) { throw new Exception("DbMigrator is not found!"); } - await CheckAndInstallDotnetEfIfNeededAsync(); + if (!IsDotNetEfToolInstalled()) + { + InstallDotnetEfTool(); + } - var output = CmdHelper.RunCmdAndGetOutput($"cd \"{commandLineArgs.Target}\" && dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\""); + var addMigrationCmd = $"cd \"{commandLineArgs.Target}\" && " + + $"dotnet ef migrations add Initial -s \"{dbMigratorProjectPath}\""; - if (output.Contains("Done.") && output.Contains("To undo this action") && output.Contains("ef migrations remove")) // Migration added successfully + var output = CmdHelper.RunCmdAndGetOutput(addMigrationCmd); + if (output.Contains("Done.") && + output.Contains("To undo this action") && + output.Contains("ef migrations remove")) { + // Migration added successfully CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run"); + await Task.CompletedTask; } else { - throw new Exception("Migrations failed: " + output); + var exceptionMsg = "Migrations failed! The following command didn't run successfully:" + + Environment.NewLine + + addMigrationCmd + + Environment.NewLine + output; + + Logger.LogError(exceptionMsg); + throw new Exception(exceptionMsg); } } - private async Task CheckAndInstallDotnetEfIfNeededAsync() + private static bool IsDotNetEfToolInstalled() { var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g"); + return output.Contains("dotnet-ef"); + } - if (output.Contains("dotnet-ef")) - { - return; - } - + private void InstallDotnetEfTool() + { Logger.LogInformation("Installing dotnet-ef tool..."); - CmdHelper.RunCmd("dotnet tool install --global dotnet-ef"); - Logger.LogInformation("dotnet-ef tool is installed."); } - private string GetDbMigratorProjectPath(string dbMigrationsFolderPath) + private static string GetDbMigratorProjectPath(string dbMigrationsFolderPath) { var srcFolder = Directory.GetParent(dbMigrationsFolderPath); + var dbMigratorDirectory = Directory.GetDirectories(srcFolder.FullName) + .FirstOrDefault(d => d.EndsWith(".DbMigrator")); - var dbMigratorFolderPath = Directory.GetDirectories(srcFolder.FullName).FirstOrDefault(d => d.EndsWith(".DbMigrator")); - - if (dbMigratorFolderPath == null) - { - return null; - } - - return Directory.GetFiles(dbMigratorFolderPath).FirstOrDefault(f => f.EndsWith(".csproj")); + return dbMigratorDirectory == null + ? null + : Directory.GetFiles(dbMigratorDirectory).FirstOrDefault(f => f.EndsWith(".csproj")); } public string GetUsageInfo() @@ -89,4 +95,4 @@ namespace Volo.Abp.Cli.Commands return string.Empty; } } -} +} \ No newline at end of file From 5dd1db0823c729db949229c5622586f7493a0641 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 23 Feb 2021 10:23:32 +0300 Subject: [PATCH 04/10] remove duplicate BookModule heading --- docs/en/Tutorials/Part-2.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 7bc42b1fc9..5ec23ba43a 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -344,8 +344,6 @@ It's time to create something visible and usable! There are some tools that we w - [Ng Bootstrap](https://ng-bootstrap.github.io/#/home) will be used as the UI component library. - [Ngx-Datatable](https://swimlane.gitbook.io/ngx-datatable/) will be used as the datatable library. -### BookModule - Run the following command line to create a new module, named `BookModule` in the root folder of the angular application: ```bash From 5a96082252878e38b1dc8412e0f1fd2a26e76dcc Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 23 Feb 2021 10:25:21 +0300 Subject: [PATCH 05/10] remove dot before image paths --- docs/en/Tutorials/Part-2.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 5ec23ba43a..1987ca7e7a 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -103,7 +103,7 @@ Before starting to the UI development, we first want to prepare the localization Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project: -![bookstore-localization-files](./images/bookstore-localization-files-v2.png) +![bookstore-localization-files](images/bookstore-localization-files-v2.png) Open the `en.json` (*the English translations*) file and change the content as below: @@ -157,7 +157,7 @@ It's time to create something visible and usable! Instead of classic MVC, we wil Create `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project. Add a new Razor Page by right clicking the Books folder then selecting **Add > Razor Page** menu item. Name it as `Index`: -![bookstore-add-index-page](./images/bookstore-add-index-page-v2.png) +![bookstore-add-index-page](images/bookstore-add-index-page-v2.png) Open the `Index.cshtml` and change the whole content as shown below: @@ -208,7 +208,7 @@ context.Menu.AddItem( Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu: -![bookstore-menu-items](./images/bookstore-new-menu-item.png) +![bookstore-menu-items](images/bookstore-new-menu-item.png) When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page. @@ -250,7 +250,7 @@ Change the `Pages/Books/Index.cshtml` as following: Create an `Index.js` file under the `Pages/Books` folder: -![bookstore-index-js-file](./images/bookstore-index-js-file-v3.png) +![bookstore-index-js-file](images/bookstore-index-js-file-v3.png) The content of the file is shown below: @@ -455,7 +455,7 @@ abp generate-proxy This command will create the following files under the `/src/app/proxy/books` folder: -![Generated files](./images/generated-proxies-3.png) +![Generated files](images/generated-proxies-3.png) ### BookComponent @@ -529,7 +529,7 @@ Open the `/src/app/book/book.component.html` and replace the content as below: Now you can see the final result on your browser: -![Book list final result](./images/bookstore-book-list.png) +![Book list final result](images/bookstore-book-list.png) {{else if UI == "Blazor"}} From 7fda88a486fd8583e13ad6c79d4380eb3d994a72 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 23 Feb 2021 10:29:56 +0300 Subject: [PATCH 06/10] make list service link in tutorial relative --- docs/en/Tutorials/Part-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 1987ca7e7a..627b55a476 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -488,7 +488,7 @@ export class BookComponent implements OnInit { ``` * We imported and injected the generated `BookService`. -* We are using the [ListService](https://docs.abp.io/en/abp/latest/UI/Angular/List-Service), a utility service of the ABP Framework which provides easy pagination, sorting and searching. +* We are using the [ListService](../UI/Angular/List-Service.md), a utility service of the ABP Framework which provides easy pagination, sorting and searching. Open the `/src/app/book/book.component.html` and replace the content as below: From 3e9e90e3d0da714faa616ffcc31dc0c9bb30bb4f Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 23 Feb 2021 11:21:35 +0300 Subject: [PATCH 07/10] remove dot before image paths --- docs/en/Tutorials/Part-3.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index e58857ec89..1190c31b0f 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -48,13 +48,13 @@ This part is also recorded as a video tutorial and ** Date: Tue, 23 Feb 2021 11:22:08 +0300 Subject: [PATCH 08/10] avoid confusion about bookTypeOptions import comment --- docs/en/Tutorials/Part-3.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en/Tutorials/Part-3.md b/docs/en/Tutorials/Part-3.md index 1190c31b0f..ec326ddb65 100644 --- a/docs/en/Tutorials/Part-3.md +++ b/docs/en/Tutorials/Part-3.md @@ -732,8 +732,7 @@ Open `/src/app/book/book.component.ts` and replace the content as below: ```js import { ListService, PagedResultDto } from '@abp/ng.core'; import { Component, OnInit } from '@angular/core'; -// import bookTypeOptions from @proxy/books -import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; +import { BookService, BookDto, bookTypeOptions } from '@proxy/books'; // add bookTypeOptions import { FormGroup, FormBuilder, Validators } from '@angular/forms'; // add this @Component({ From 7539e9b3aaad1577252610ceb9175df96698295e Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 23 Feb 2021 12:37:00 +0300 Subject: [PATCH 09/10] fix note about localization key names --- docs/en/Tutorials/Part-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-5.md b/docs/en/Tutorials/Part-5.md index 29374419cb..213dd8821b 100644 --- a/docs/en/Tutorials/Part-5.md +++ b/docs/en/Tutorials/Part-5.md @@ -117,7 +117,7 @@ Finally, edit the localization file (`en.json` under the `Localization/BookStore "Permission:Books.Delete": "Deleting the books" ```` -> Localization key names are arbitrary and no forcing rule. But we prefer the convention used above. +> Localization key names are arbitrary and there is no forcing rule. But we prefer the convention used above. ### Permission Management UI From 9d65eb81ddfba35749d69e2308451669af5bf44f Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Tue, 23 Feb 2021 12:45:04 +0300 Subject: [PATCH 10/10] fix spelling and grammar --- docs/en/Tutorials/Part-6.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/Tutorials/Part-6.md b/docs/en/Tutorials/Part-6.md index 69c6574520..d5df8133b7 100644 --- a/docs/en/Tutorials/Part-6.md +++ b/docs/en/Tutorials/Part-6.md @@ -124,7 +124,7 @@ Created this class inside the `Acme.BookStore.Domain.Shared` project since we wi ## AuthorManager: The Domain Service -`Author` constructor and `ChangeName` method is `internal`, so they can be usable only in the domain layer. Create an `AuthorManager` class in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: +`Author` constructor and `ChangeName` methods are `internal`, so they can be used only in the domain layer. Create an `AuthorManager` class in the `Authors` folder (namespace) of the `Acme.BookStore.Domain` project: ````csharp using System; @@ -186,7 +186,7 @@ namespace Acme.BookStore.Authors * `AuthorManager` forces to create an author and change name of an author in a controlled way. The application layer (will be introduced later) will use these methods. -> **DDD tip**: Do not introduce domain service methods unless they are really needed and perform some core business rules. For this case, we needed to this service to be able to force the unique name constraint. +> **DDD tip**: Do not introduce domain service methods unless they are really needed and perform some core business rules. For this case, we needed this service to be able to force the unique name constraint. Both methods checks if there is already an author with the given name and throws a special business exception, `AuthorAlreadyExistsException`, defined in the `Acme.BookStore.Domain` project (in the `Authors` folder) as shown below: @@ -226,7 +226,7 @@ This is a unique string represents the error code thrown by your application and "BookStore:00001": "There is already an author with the same name: {name}" ```` -Whenever you throw an `AuthorAlreadyExistsException`, the end use will see a this message on the UI. +Whenever you throw an `AuthorAlreadyExistsException`, the end user will see a nice error message on the UI. ## IAuthorRepository