Browse Source

Merge branch 'dev' into 2294-Dynamic-CSharp-Api-Clients-net-standard20

pull/2867/head
Halil İbrahim Kalkan 7 years ago
committed by GitHub
parent
commit
49299861f3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  2. 28
      docs/en/CLI.md
  3. 514
      docs/en/Entity-Framework-Core-Migrations.md
  4. BIN
      docs/en/_resources/Diagrams.docx
  5. 4
      docs/en/docs-nav.json
  6. BIN
      docs/en/images/bookstore-second-database.png
  7. BIN
      docs/en/images/multiple-database-usage.png
  8. BIN
      docs/en/images/pmc-add-migration-initial-update-database.png
  9. 666
      docs/zh-Hans/Entity-Framework-Core-Migrations.md
  10. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md
  11. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md
  12. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md
  13. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png
  14. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png
  15. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png
  16. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png
  17. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png
  18. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png
  19. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png
  20. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table.png
  21. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog-2.png
  22. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog.png
  23. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png
  24. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-homepage.png
  25. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png
  26. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png
  27. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png
  28. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png
  29. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png
  30. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png
  31. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png
  32. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png
  33. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png
  34. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png
  35. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-user-management.png
  36. 0
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png
  37. 0
      docs/zh-Hans/Tutorials/Index.md
  38. BIN
      docs/zh-Hans/_resources/Diagrams.docx
  39. 5
      docs/zh-Hans/docs-nav.json
  40. BIN
      docs/zh-Hans/images/bookstore-second-database.png
  41. BIN
      docs/zh-Hans/images/multiple-database-usage.png
  42. BIN
      docs/zh-Hans/images/pmc-add-migration-initial-update-database.png
  43. BIN
      docs/zh-Hans/images/pmc-add-migration-role-title.png
  44. 21
      framework/Volo.Abp.sln
  45. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/AlertsDemo/AlertsDemoViewComponent.cs
  46. 70
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/AlertsDemo/Default.cshtml
  47. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BadgesDemo/BadgesDemoViewComponent.cs
  48. 49
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BadgesDemo/Default.cshtml
  49. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BordersDemo/BordersDemoViewComponent.cs
  50. 46
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BordersDemo/Default.cshtml
  51. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BreadcrumbsDemo/BreadcrumbsDemoViewComponent.cs
  52. 18
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BreadcrumbsDemo/Default.cshtml
  53. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ButtonGroupsDemo/ButtonGroupsDemoViewComponent.cs
  54. 68
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ButtonGroupsDemo/Default.cshtml
  55. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CardsDemo/CardsDemoViewComponent.cs
  56. 156
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CardsDemo/Default.cshtml
  57. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CollapseDemo/CollapseDemoViewComponent.cs
  58. 43
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CollapseDemo/Default.cshtml
  59. 171
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/DropdownsDemo/Default.cshtml
  60. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/DropdownsDemo/DropdownsDemoViewComponent.cs
  61. 223
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/GridsDemo/Default.cshtml
  62. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/GridsDemo/GridsDemoViewComponent.cs
  63. 82
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ListGroupsDemo/Default.cshtml
  64. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ListGroupsDemo/ListGroupsDemoViewComponent.cs
  65. 15
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ModalsDemo/Default.cshtml
  66. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ModalsDemo/ModalsDemoViewComponent.cs
  67. 53
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/NavsDemo/Default.cshtml
  68. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/NavsDemo/NavsDemoViewComponent.cs
  69. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/PopoversDemo/Default.cshtml
  70. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/PopoversDemo/PopoversDemoViewComponent.cs
  71. 17
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ProgressBarsDemo/Default.cshtml
  72. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ProgressBarsDemo/ProgressBarsDemoViewComponent.cs
  73. 134
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TablesDemo/Default.cshtml
  74. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TablesDemo/TablesDemoViewComponent.cs
  75. 63
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TabsDemo/Default.cshtml
  76. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TabsDemo/TabsDemoViewComponent.cs
  77. 23
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TooltipsDemo/Default.cshtml
  78. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TooltipsDemo/TooltipsDemoViewComponent.cs
  79. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs
  80. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs
  81. 0
      framework/src/Volo.Abp.BackgroundJobs.Quartz/FodyWeavers.xml
  82. 0
      framework/src/Volo.Abp.BackgroundJobs.Quartz/FodyWeavers.xsd
  83. 22
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo.Abp.BackgroundJobs.Quartz.csproj
  84. 18
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobsQuartzModule.cs
  85. 27
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs
  86. 36
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs
  87. 0
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/FodyWeavers.xml
  88. 0
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/FodyWeavers.xsd
  89. 24
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo.Abp.BackgroundWorkers.Quartz.csproj
  90. 30
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs
  91. 27
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpQuartzConventionalRegistrar.cs
  92. 11
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorker.cs
  93. 14
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerBase.cs
  94. 43
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs
  95. 30
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs
  96. 81
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs
  97. 27
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  98. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/AbpNuGetIndexUrlService.cs
  99. 83
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs
  100. 25
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs

4
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -154,6 +154,8 @@
"SeeTheDocumentForMoreInformation": "See the <a href=\"{1}\">{0} document</a> for more information",
"IndexPageHeroSection": "<span class=\"first-line shine\"><strong>open source</strong></span><span class=\"second-line text-uppercase\">Web Application<br />Framework </span><span class=\"third-line shine2\"><strong>for asp.net core</strong></span>",
"UiFramework": "UI Framework",
"EmailAddress": "Email address"
"EmailAddress": "Email address",
"Mobile": "Mobile",
"ReactNative": "React Native"
}
}

28
docs/en/CLI.md

@ -128,9 +128,35 @@ abp update [options]
* `--npm`: Only updates NPM packages.
* `--nuget`: Only updates NuGet packages.
### switch-to-preview
You can use this command to switch your project to latest preview version of the ABP framework packages.
Usage:
````bash
abp switch-to-preview [options]
````
#### Options
`--solution-path` or `-sp`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
### switch-to-stable
If you're using the ABP Framework preview packages, you can switch back to stable version using this command.
Usage:
````bash
abp switch-to-stable [options]
````
#### Options
`--solution-path` or `-sp`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
### login
Some features of the CLI requires to be logged in to abp.io platform. To login with your username write
Some features of the CLI requires to be logged in to abp.io platform. To login with your username write:
```bash
abp login <username>

514
docs/en/Entity-Framework-Core-Migrations.md

@ -1,9 +1,13 @@
# EF Core Advanced Database Migrations
# EF Core Database Migrations
This document begins by **introducing the default structure** provided by [the application startup template](Startup-Templates/Application.md) and **discusses various scenarios** you may want to implement for your own application.
> This document is for who want to fully understand and customize the database structure comes with [the application startup template](Startup-Templates/Application.md). If you simply want to create entities and manage your code first migrations, just follow [the startup tutorials](Tutorials/Index.md).
### Source Code
You can find the source code of the example project referenced by this document [here](https://github.com/abpframework/abp/tree/dev/samples/EfCoreMigrationDemo). However, you need to read and understand this document in order to understand the example project's source code.
## About the EF Core Code First Migrations
Entity Framework Core provides an easy to use and powerful [database migration system](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). ABP Framework [startup templates](Startup-Templates/Index.md) take the advantage of this system to allow you to develop your application in a standard way.
@ -20,17 +24,19 @@ When you [create a new web application](https://abp.io/get-started) (with EF Cor
![bookstore-visual-studio-solution-v3](images/bookstore-visual-studio-solution-v3.png)
> Actual solution structure may be a bit different based on your preferences, but the database part will be same.
Actual solution structure may be a bit different based on your preferences, but the database part will be same.
> This document will use the `Acme.BookStore` example project name to refer the projects and classes. You need to find the corresponding class/project in your solution.
### The Database Structure
The startup template has some [application modules](Modules/Index.md) pre-installed. Each layer of the solution has corresponding module package references. So, the `.EntityFrameworkCore` project has the NuGet references for the `.EntityFrameworkCore` packages of the used modules:
The startup template has some [application modules](Modules/Index.md) pre-installed. Each layer of the solution has corresponding module **package references**. So, the `.EntityFrameworkCore` project has the NuGet references for the `.EntityFrameworkCore` packages of the used modules:
![bookstore-efcore-dependencies](images/bookstore-efcore-dependencies.png)
In this way, you collect all the EF Core dependencies under the `.EntityFrameworkCore` project.
In this way, you collect all the **EF Core dependencies** under the `.EntityFrameworkCore` project.
> In addition to the module references, it references to the `Volo.Abp.EntityFrameworkCore.SqlServer` package since the startup template is pre-configured for the SQL Server. See the documentation if you want to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
> In addition to the module references, it references to the `Volo.Abp.EntityFrameworkCore.SqlServer` package since the startup template is pre-configured for the **SQL Server**. See the documentation if you want to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
While every module has its own `DbContext` class by design and can use its **own physical database**, the solution is configured to use a **single shared database** as shown in the figure below:
@ -57,25 +63,25 @@ ABP Framework's [connection string](Connection-Strings.md) system allows you to
The example configuration about tells to the ABP Framework to use the second connection string for the [Audit Logging module](Modules/Audit-Logging.md).
However, this is just the beginning. You also need to create the second database, create audit log tables inside it and maintain the database tables using the code first approach. One of the main purposes of this document is to guide you on such database separation scenarios.
**However, this is just the beginning**. You also need to create the second database, create audit log tables inside it and maintain the database tables using the code first migrations approach. One of the main purposes of this document is to guide you on such **database separation** scenarios.
#### Module Tables
Every module uses its own databases tables. For example, the [Identity Module](Modules/Identity.md) has some tables to manage the users and roles in the system.
Every module uses its **own databases tables**. For example, the [Identity Module](Modules/Identity.md) has some tables to manage the users and roles in the system.
#### Table Prefixes
##### Table Prefixes
Since it is allowed to share a single database by all modules (it is the default configuration), a module typically uses a prefix to group its own tables.
Since it is allowed to share a single database by all modules (it is the default configuration), a module typically uses a **table name prefix** to group its own tables.
The fundamental modules, like [Identity](Modules/Identity.md), [Tenant Management](Modules/Tenant-Management.md) and [Audit Logs](Modules/Audit-Logging.md), use the `Abp` prefix, while some other modules use their own prefixes. [Identity Server](Modules/IdentityServer.md) module uses the `IdentityServer` prefix for example.
If you want, you can change the database table name prefix for a module for your application. Example:
If you want, you can **change the database table name prefix** for a module for your application. Example:
````csharp
Volo.Abp.IdentityServer.AbpIdentityServerDbProperties.DbTablePrefix = "Ids";
````
This code changes the prefix of the [Identity Server](Modules/IdentityServer.md) module. Write this code at the very beginning in your application.
This code changes the prefix of the [Identity Server](Modules/IdentityServer.md) module. Write this code **at the very beginning** in your application.
> Every module also defines `DbSchema` property (near to `DbTablePrefix`), so you can set it for the databases support the schema usage.
@ -87,7 +93,7 @@ From the database point of view, there are three important projects those will b
This project has the `DbContext` class (`BookStoreDbContext` for this sample) of your application.
Every module uses its own `DbContext` class to access to the database. Likewise, your application has its own `DbContext`. You typically use this `DbContext` in your application code (in your custom [repositories](Repositories.md) if you follow the best practices). It is almost an empty `DbContext` since your application don't have any entities at the beginning, except the pre-defined `AppUser` entity:
**Every module uses its own `DbContext` class** to access to the database. Likewise, your application has its own `DbContext`. You typically use this `DbContext` in your application code (in your custom [repositories](Repositories.md) if you follow the best practices). It is almost an empty `DbContext` since your application don't have any entities at the beginning, except the pre-defined `AppUser` entity:
````csharp
[ConnectionStringName("Default")]
@ -196,9 +202,9 @@ public class BookStoreMigrationsDbContext : AbpDbContext<BookStoreMigrationsDbCo
##### Sharing the Mapping Code
First problem is that: A module uses its own `DbContext` which needs to the database mappings. The `MigrationsDbContext` also needs to the same mapping in order to create the database tables for this module. We definitely don't want to duplicate the mapping code.
First problem is that: A module uses its own `DbContext` which needs to the database mappings. The `MigrationsDbContext` also needs to the same mapping in order to create the database tables for this module. We definitely **don't want to duplicate** the mapping code.
The solution is to define an extension method (on the `ModelBuilder`) that can be called by both `DbContext` classes. So, every module defines such an extension method.
The solution is to define an **extension method** (on the `ModelBuilder`) that can be called by both `DbContext` classes. So, all modules define such extension methods.
For example, the `builder.ConfigureBackgroundJobs()` method call configures the database tables for the [Background Jobs module](Modules/Background-Jobs.md). The definition of this extension method is something like that:
@ -235,9 +241,9 @@ public static class BackgroundJobsDbContextModelCreatingExtensions
This extension method also gets options to change the database table prefix and schema for this module, but it is not important here.
The final application calls the extension methods inside the `MigrationsDbContext` class, so it can decide which modules are included to the database maintained by this `MigrationsDbContext`. If you want to create a second database and move some module tables to the second database, then you need to have a second `MigrationsDbContext` class which only calls the extension methods of the related modules. This topic will be detailed in the next sections.
The final application calls the extension methods inside the `MigrationsDbContext` class, so it can decide which modules are included in the database maintained by this `MigrationsDbContext`. If you want to create a second database and move some module tables to the second database, then you need to have a second `MigrationsDbContext` class which only calls the extension methods of the related modules. This topic will be detailed in the next sections.
The same `ConfigureBackgroundJobs` method is also called the `DbContext` of the Background Jobs module:
The same `ConfigureBackgroundJobs` method is also called in the `DbContext` of the Background Jobs module:
````csharp
[ConnectionStringName(BackgroundJobsDbProperties.ConnectionStringName)]
@ -262,14 +268,14 @@ public class BackgroundJobsDbContext
}
````
In this way, the mapping configuration of a module can be shared between `DbContext` classes.
In this way, the mapping configuration of a module can be shared between `DbContext` classes. The code above is inside the related module NuGet package, so you don't care about it.
##### Reusing a Table of a Module
You may want to reuse a table of a depended module in your application. In this case, you have two options:
You may want to **reuse a table** of a depended module in your application. In this case, you have two options:
1. You can directly use the entity defined by the module.
2. You can create a new entity mapping to the same database table.
1. You can **directly use the entity** defined by the module.
2. You can **create a new entity** mapping to the same database table.
###### Use the Entity Defined by a Module
@ -310,13 +316,13 @@ This example injects the `IRepository<IdentityUser, Guid>` (default repository)
Working with an entity of a module is easy if you want to use the entity as is. However, you may want to define your own entity class and map to the same database table in the following cases;
* You want to add a new field to the table and map it to a property in the entity. You can't use the module's entity since it doesn't have the related property.
* You want to use a subset of the table fields. You don't want to access to all properties of the entity and hide the unrelated properties (from a security perspective or just by design).
* You don't want to directly depend on a module entity class.
* You want to **add a new field** to the table and map it to a property in the entity. You can't use the module's entity since it doesn't have the related property.
* You want to **use a subset of the table fields**. You don't want to access to all properties of the entity and hide the unrelated properties (from a security perspective or just by design).
* You don't want to directly **depend on** a module entity class.
In any case, the progress is same. Assume that you want to create an entity, named `AppRole`, mapped to the same table of the `IdentityRole` entity of the [Identity module](Modules/Identity.md).
Here, we will show the implementation, then **will discuss the limitations** (and reasons of the limitations) of this approach.
Here, we will show the implementation, then **will discuss the limitations** of this approach.
First, create a new `AppRole` class in your `.Domain` project:
@ -332,7 +338,7 @@ namespace Acme.BookStore.Roles
// Properties shared with the IdentityRole class
public Guid? TenantId { get; private set; }
public virtual string Name { get; protected internal set; }
public string Name { get; private set; }
//Additional properties
@ -347,9 +353,9 @@ namespace Acme.BookStore.Roles
````
* It's inherited from [the `AggregateRoot<Guid>` class](Entities.md) and implements [the `IMultiTenant` interface](Multi-Tenancy.md) because the `IdentityRole` also does the same.
* You can add any properties defined by the `IdentityRole` entity. This examples add only the `TenantId` and `Name` properties since we only need them here.
* You can add any properties defined by the `IdentityRole` entity. This examples add only the `TenantId` and `Name` properties since we only need them here. You can make the setters private (like in this example) to prevent changing Identity module's properties accidently.
* You can add custom (additional) properties. This example adds the `Title` property.
* The constructor is provide, so it is not allowed to directly create a new `AppRole` entity. Creating a role is a responsibility of the Identity module. You can query roles, set/update your custom properties, but you should not create or delete a role in your code, as a best practice (while there is nothing restricts you).
* The **constructor is private**, so it is not allowed to directly create a new `AppRole` entity. Creating a role is a responsibility of the Identity module. You can query roles, set/update your custom properties, but you should not create or delete a role in your code, as a best practice (while there is nothing restricts you).
Now, it is time to define the EF Core mappings. Open the `DbContext` of your application (`BookStoreDbContext` in this sample) and add the following property:
@ -400,7 +406,7 @@ builder.Entity<AppRole>(b =>
* It maps to the same `AbpRoles` table shared with the `IdentityRole` entity.
* `ConfigureByConvention()` configures the standard/base properties (like `TenantId`) and recommended to always call it.
`ConfigureCustomRoleProperties()` has not exists yet. Define it inside the `BookStoreDbContextModelCreatingExtensions` class (near to your `DbContext` in the `EntityFrameworkCore` project):
`ConfigureCustomRoleProperties()` has not exists yet. Define it inside the `BookStoreDbContextModelCreatingExtensions` class (near to your `DbContext` in the `.EntityFrameworkCore` project):
````csharp
public static void ConfigureCustomRoleProperties<TRole>(this EntityTypeBuilder<TRole> b)
@ -410,10 +416,12 @@ public static void ConfigureCustomRoleProperties<TRole>(this EntityTypeBuilder<T
}
````
* This method only defines the custom properties of your entity.
* Unfortunately, we can not utilize the fully type safety here (by referencing the `AppRole` entity). The best we can do is to use the `Title` name as type safe.
* This method only defines the **custom properties** of your entity.
* Unfortunately, we can not utilize the fully **type safety** here (by referencing the `AppRole` entity). The best we can do is to use the `Title` name as type safe. This is because of EF Core migration system can not map two unrelated entity classes to the same database table.
You've configured the custom property for your `DbContext` used by your application on the runtime. We also need to configure the `MigrationsDbContext`.
You've configured the custom property for your `DbContext` used by your application on the runtime. We also need to configure the `MigrationsDbContext`. Open the `MigrationsDbContext` (`BookStoreMigrationsDbContext` for this example) and change as shown below:
Open the `MigrationsDbContext` (`BookStoreMigrationsDbContext` for this example) and change as shown below:
````csharp
protected override void OnModelCreating(ModelBuilder builder)
@ -451,7 +459,7 @@ builder.Entity<IdentityRole>(b =>
In this way, we re-used the extension method that is used to configure custom property mappings for the role. But, this time, did the same customization for the `IdentityRole` entity.
Now, you can add a new EF Core database migration using the standard `Add-Migration` command in the Package Manager Console (remember to select `.EntityFrameworkCore.DbMigrations` as the Default Project in the PMC):
Now, you can add a new EF Core database migration using the standard `Add-Migration` command in the Package Manager Console (remember to select `.EntityFrameworkCore.DbMigrations` as the Default Project in the PMC and make sure that the `.Web` project is still the startup project):
![pmc-add-migration-role-title](images/pmc-add-migration-role-title.png)
@ -480,6 +488,444 @@ public partial class Added_Title_To_Roles : Migration
All done! Just run the `Update-Database` command in the PMC or run the `.DbMigrator` project in your solution to apply changes to database.
##### Discussion of an Alternative Scenario: Every Module Manages Its Own Migration Path
Now, you can work with the `AppRole` entity just like any other entity of your application. An example [application service](Application-Services.md) that queries and updates roles:
````csharp
public class AppRoleAppService : ApplicationService, IAppRoleAppService
{
private readonly IRepository<AppRole, Guid> _appRoleRepository;
public AppRoleAppService(IRepository<AppRole, Guid> appRoleRepository)
{
_appRoleRepository = appRoleRepository;
}
public async Task<List<AppRoleDto>> GetListAsync()
{
var roles = await _appRoleRepository.GetListAsync();
return roles
.Select(r => new AppRoleDto
{
Id = r.Id,
Name = r.Name,
Title = r.Title
})
.ToList();
}
public async Task UpdateTitleAsync(Guid id, string title)
{
var role = await _appRoleRepository.GetAsync(id);
role.Title = title;
await _appRoleRepository.UpdateAsync(role);
}
}
````
There are some **limitations** of creating a new entity and mapping it to a table of a depended module:
* Your **custom properties must be nullable**. For example, `AppRole.Title` was nullable here. Otherwise, Identity module throws exception because it doesn't know and can not fill the Title when it inserts a new role to the database.
* As a good practice, you should not update the **properties defined by the module**, especially if it requires a business logic. You typically want to manage your own properties.
##### Alternative Approaches
Instead of creating a new entity class to add a custom property, you can use the following approaches.
###### Using the ExtraProperties
All entities derived from the `AggregateRoot ` class can store name-value pairs in their `ExtraProperties` property, which is a `Dictionary<string, object>` serialized to JSON in the database table. So, you can add values to this dictionary and query again without changing the entity.
For example, you can store query the title Property inside an `IdentityRole` instead of creating a new entity. Example:
````csharp
public class IdentityRoleExtendingService : ITransientDependency
{
private readonly IIdentityRoleRepository _identityRoleRepository;
public IdentityRoleExtendingService(IIdentityRoleRepository identityRoleRepository)
{
_identityRoleRepository = identityRoleRepository;
}
public async Task<string> GetTitleAsync(Guid id)
{
var role = await _identityRoleRepository.GetAsync(id);
return role.GetProperty<string>("Title");
}
public async Task SetTitleAsync(Guid id, string newTitle)
{
var role = await _identityRoleRepository.GetAsync(id);
role.SetProperty("Title", newTitle);
await _identityRoleRepository.UpdateAsync(role);
}
}
````
* `GetProperty` and `SetProperty` methods are shortcuts to get and set a value in the `role.ExtraProperties` dictionary and they are the recommended way to work with the extra properties.
In this way, you can easily attach any type of value to an entity of a depended module. However, there are some drawbacks of this usage:
* All the extra properties are stored as **a single JSON object** in the database. They are not stored as new table fields, as you may expect. Creating database table indexes and using SQL queries against these properties will be harder compared to simple table fields.
* Property names are strings, so they are **not type safe**. It is recommended to define constants for these kind of properties to prevent typo errors.
###### Creating a New Table
Instead of creating a new entity and mapping to the same table, you can also create **your own table** to store your properties. You typically duplicate some values of the original entity. For example, you can add `Name` field to your own table which is a duplication of the `Name` field in the original table.
In this case, you don't deal with migration problems, however you need to deal with the problems of data duplication. When the duplicated value changes, you should reflect the same change in your table. You can use local or distributed [event bus](Event-Bus.md) to subscribe to the change events for the original entity. This is the recommended way of depending on a microservice's data from another microservice, especially if they have separate physical databases (you can search on the web on data sharing on a microservice design, it is a wide topic to cover here).
#### Discussion of an Alternative Scenario: Every Module Manages Its Own Migration Path
As mentioned before, `.EntityFrameworkCore.DbMigrations` merges all the database mappings of all the modules (plus your application's mappings) to create a unified migration path.
An alternative approach would be to allow each module to have its own migrations to maintain its database tables. While it seems more module in the beginning, it has some important drawbacks:
* **EF Core migration system depends on the DBMS provider**. For example, if a module has created migrations for SQL Server, then you can not use this migration code for MySQL. It is not practical for a module to maintain migrations for all available DBMS providers. Leaving the migration to the application code (as explained in this document) allows you to **choose the DBMS in the application** code.
* It would be harder or impossible to **share a table** between modules or **re-use a table** of a module in your application. Because EF Core migration system can not handle it and will throw exceptions like "Table XXX is already exists in the database".
* It would be harder to **customize/enhance** the mapping and the resulting migration code.
* It would be harder to track and **apply changes** to database when you use multiple modules.
## Using Multiple Databases
The default startup template is organized to use a single database used by all the modules and by your application. However, the ABP Framework and all the pre-built modules are designed so that **they can use multiple databases**. Each module can use its own database or you can group modules into a few databases.
This section will explain how to move Audit Logging, Setting Management and Permission Management module tables to a **second database** while the remaining modules continue to use the main ("Default") database.
The resulting structure will be like the figure below:
![single-database-usage](images/multiple-database-usage.png)
### Change the Connection Strings Section
First step is to change the connection string section inside all the `appsettings.json` files. Initially, it is like that:
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true"
}
````
Change it as shown below:
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true",
"AbpPermissionManagement": "Server=localhost;Database=BookStore_SecondDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"AbpSettingManagement": "Server=localhost;Database=BookStore_SecondDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"AbpAuditLogging": "Server=localhost;Database=BookStore_SecondDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
````
Added **three more connection strings** for the related module to target the `BookStore_SecondDb` database (they are all same). For example, `AbpPermissionManagement` is the connection string for the permission management module.
The `AbpPermissionManagement` is a constant [defined](https://github.com/abpframework/abp/blob/97eaa6ff5a044f503465455c86332e5a277b077a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDbProperties.cs#L11) by the permission management module. ABP Framework [connection string selection system](Connection-Strings.md) selects this connection string for the permission management module if you define. If you don't define, it fallbacks to the `Default` connection string.
### Create a Second Migration Project
Defining the connection strings as explained above is enough **on runtime**. However, `BookStore_SecondDb` database doesn't exist yet. You need to create the database and the tables for the related modules.
Just like the main database, we want to use the EF Core Code First migration system to create and maintain the second database.
An easy way is to create a second project (`.csproj`) for the second migration `DbContext`.
So, create a new **class library project** in your solution named `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb` (or name it better if you didn't like it).
The `.csproj` content should be something like that:
````xml
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Acme.BookStore.DbMigrationsForSecondDb</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Acme.BookStore.EntityFrameworkCore\Acme.BookStore.EntityFrameworkCore.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0" />
</ItemGroup>
</Project>
````
You can just copy & modify the content of the original `.DbMigrations` project. This project references to the `.EntityFrameworkCore` project. **Only difference** is the `RootNamespace` value.
**Add a reference** to this project from the `.Web` project (otherwise, EF Core tooling doesn't allow to use the `Add-Migration` command).
### Create the Second DbMigrationDbContext
Create a new `DbContext` for the migrations and call the **extension methods** of the modules to configure the database tables for the related modules:
````csharp
[ConnectionStringName("AbpPermissionManagement")]
public class BookStoreSecondMigrationsDbContext :
AbpDbContext<BookStoreSecondMigrationsDbContext>
{
public BookStoreSecondMigrationsDbContext(
DbContextOptions<BookStoreSecondMigrationsDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureAuditLogging();
}
}
````
> `[ConnectionStringName(...)]` attribute is important here and tells to the ABP Framework which connection string should be used for this `DbContext`. We've used `AbpPermissionManagement`, but all are the same.
Create a **Design Time Db Factory** class, that is used by the EF Core tooling (by `Add-Migration` and `Update-Database` PCM commands for example):
````csharp
/* This class is needed for EF Core console commands
* (like Add-Migration and Update-Database commands) */
public class BookStoreSecondMigrationsDbContextFactory
: IDesignTimeDbContextFactory<BookStoreSecondMigrationsDbContext>
{
public BookStoreSecondMigrationsDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<BookStoreSecondMigrationsDbContext>()
.UseSqlServer(configuration.GetConnectionString("AbpPermissionManagement"));
return new BookStoreSecondMigrationsDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
````
This is similar to the class inside the `.EntityFrameworCore.DbMigrations` project, except this one uses the `AbpPermissionManagement` connection string.
Now, you can open the Package Manager Console, select the `.EntityFrameworkCore.DbMigrationsForSecondDb` project as the default project (make sure the `.Web` project is still the startup project) and run the `Add-Migration "Initial"` and `Update-Database` commands as shown below:
![pmc-add-migration-initial-update-database](images/pmc-add-migration-initial-update-database.png)
Now, you should have a new database contains only the tables needed by the related modules:
![bookstore-second-database](images/bookstore-second-database.png)
### Remove Modules from the Main Database
We've **created a second database** contains tables for the Audit Logging, Permission Management and Setting Management modules. So, we should **delete these tables from the main database**. It is pretty easy.
First, remove the following lines from the `MigrationsDbContext` class (`BookStoreMigrationsDbContext` for this example):
````csharp
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureAuditLogging();
````
Open the Package Manager Console, select the `.EntityFrameworkCore.DbMigrations` as the Default project (make sure that the `.Web` project is still the startup project) and run the following command:
````
Add-Migration "Removed_Audit_Setting_Permission_Modules"
````
This command will create a new migration class as shown below:
````csharp
public partial class Removed_Audit_Setting_Permission_Modules : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpAuditLogActions");
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpPermissionGrants");
migrationBuilder.DropTable(
name: "AbpSettings");
migrationBuilder.DropTable(
name: "AbpEntityChanges");
migrationBuilder.DropTable(
name: "AbpAuditLogs");
}
...
}
````
Be careful in this step:
* If you have a **live system**, then you should care about the **data loss**. You need to move the table contents to the second database before deleting the tables.
* If you **haven't started** your project yet, you can consider to **remove all the migrations** and re-create the initial one to have a cleaner migration history.
Run the `Update-Database` command to delete the tables from your main database.
Notice that you've also **deleted some initial seed data** (for example, permission grants for the admin role) if you haven't copied it to the new database. If you run the application, you may not login anymore. The solution is simple: **Re-run the `.DbMigrator` console application** in your solution, it will seed the new database.
### Automate the Second Database Schema Migration
`.DbMigrator` console application can run the database seed code across multiple databases, without any additional configuration. However, it can not run the EF Core Code First Migrations inside the second database migration project. Now, you will see how to configure the console migration application to handle both databases.
#### Implementing the IBookStoreDbSchemaMigrator
`EntityFrameworkCoreBookStoreDbSchemaMigrator` class inside the `Acme.BookStore.EntityFrameworkCore.DbMigrations` project is responsible to migrate the database schema for the `BookStoreMigrationsDbContext`. It should be like that:
````csharp
[Dependency(ReplaceServices = true)]
public class EntityFrameworkCoreBookStoreDbSchemaMigrator
: IBookStoreDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreBookStoreDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We are intentionally resolving the BookStoreMigrationsDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<BookStoreMigrationsDbContext>()
.Database
.MigrateAsync();
}
}
````
It implements the `IBookStoreDbSchemaMigrator` and **replaces existing services** (see the first line).
Remove the `[Dependency(ReplaceServices = true)]` line, because we will have two implementations of this interface and we want to use both. We don't want to replace one of them.
Create a copy of this class inside the new migration project (`Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb`), but use the `BookStoreSecondMigrationsDbContext`. Example implementation:
````csharp
public class EntityFrameworkCoreSecondBookStoreDbSchemaMigrator
: IBookStoreDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreSecondBookStoreDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We are intentionally resolving the BookStoreSecondMigrationsDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<BookStoreSecondMigrationsDbContext>()
.Database
.MigrateAsync();
}
}
````
> Name of this class is important for [dependency injection](Dependency-Injection.md). It should end with `BookStoreDbSchemaMigrator` to be injectable by `IBookStoreDbSchemaMigrator` reference.
We, now, have two implementations of the `IBookStoreDbSchemaMigrator` interface, each one responsible to migrate the related database schema.
#### Define a Module Class for the Second Migration Project
It is time to define the [module](Module-Development-Basics.md) class for this second migrations (`Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb`) project:
````csharp
[DependsOn(
typeof(BookStoreEntityFrameworkCoreModule)
)]
public class BookStoreEntityFrameworkCoreSecondDbMigrationsModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<BookStoreSecondMigrationsDbContext>();
}
}
````
Now, reference `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb` project from the `Acme.BookStore.DbMigrator` project and `typeof(BookStoreEntityFrameworkCoreSecondDbMigrationsModule)` to the dependency list of the `BookStoreDbMigratorModule`. `BookStoreDbMigratorModule` class should be something like that:
````csharp
[DependsOn(
typeof(AbpAutofacModule),
typeof(BookStoreEntityFrameworkCoreDbMigrationsModule),
typeof(BookStoreEntityFrameworkCoreSecondDbMigrationsModule), // ADDED THIS!
typeof(BookStoreApplicationContractsModule)
)]
public class BookStoreDbMigratorModule : AbpModule
{
...
}
````
We had a reference to the `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb` project from the `Acme.BookStore.Web` project, but hadn't added module dependency since we hadn't created it before. But, now we have it and we need to add `typeof(BookStoreEntityFrameworkCoreSecondDbMigrationsModule)` to the dependency list of the `BookStoreWebModule` class.
#### BookStoreDbMigrationService
You need one final touch to the `BookStoreDbMigrationService` inside the `Acme.BookStore.Domain` project. It is currently designed to work with a single `IBookStoreDbSchemaMigrator` implementation, but now we have two.
It injects `IBookStoreDbSchemaMigrator`. Replace it with an `IEnumerable<IBookStoreDbSchemaMigrator>` injection ([Dependency Injection System](Dependency-Injection.md) allows to inject multiple implementations of an interface just like that).
Now, you have **a collection of schema migrators** instead of a single one. Find the lines like:
````csharp
await _dbSchemaMigrators.MigrateAsync();
````
change them to:
````csharp
foreach (var migrator in _dbSchemaMigrators)
{
await migrator.MigrateAsync();
}
````
You can run the `.DbMigrator` application to migrate & seed the databases. To test, you can delete both databases and run the `.DbMigrator` application again to see if it creates both of the databases.
## Conclusion
This document explains how to split your databases and manage your database migrations of your solution for Entity Framework Core. In brief, you need to have a separate migration project per different databases.
## Source Code
TODO
You can find the source code of the example project referenced by this document [here](https://github.com/abpframework/abp/tree/dev/samples/EfCoreMigrationDemo). However, you need to read and understand this document in order to understand the example project's source code.

BIN
docs/en/_resources/Diagrams.docx

Binary file not shown.

4
docs/en/docs-nav.json

@ -295,6 +295,10 @@
"text": "Entity Framework Core",
"path": "Entity-Framework-Core.md",
"items": [
{
"text": "Database Migrations",
"path": "Entity-Framework-Core-Migrations.md"
},
{
"text": "Switch DBMS",
"path": "Entity-Framework-Core-Other-DBMS.md",

BIN
docs/en/images/bookstore-second-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/en/images/multiple-database-usage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/en/images/pmc-add-migration-initial-update-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

666
docs/zh-Hans/Entity-Framework-Core-Migrations.md

@ -1,15 +1,19 @@

# EF核心高级数据库迁移
# EF Core数据库迁移
本文首先介绍[应用程序启动模板](Startup-Templates/Application.md)提供的**默认结构**,并讨论您可能希望为自己的应用程序实现的**各种场景**.
> 本文档适用于希望完全理解和自定义[应用程序启动模板](Startup-Templates/Application.md)附带的数据库结构的人员. 如果你只是想创建实体和管理代码优先(code first)迁移,只需要遵循[启动教程](Tutorials/Index.md).
### 源码
你可以在[这里](https://github.com/abpframework/abp/tree/dev/samples/EfCoreMigrationDemo)找到本文引用的示例项目的源代码; 但是为了理解示例项目的源代码,你需要阅读和理解这个文档.
## 关于EF Core 代码优先迁移
Entity Framework Core 提供了一种简单强大[数据库迁移系统](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). ABP框架[启动模板](Startup-Templates/Index.md)使用这个系统,让你以标准的方式开发你的应用程序.
但是EF Core迁移系统在[模块化环境中不是很好],在模块化环境中,每个模块都维护**自己的数据库架构**,而实际上两个或多个模块可以**共享一个数据库**.
但是EF Core迁移系统在**模块化环境中不是很好**,在模块化环境中,每个模块都维护**自己的数据库模式**,而实际上两个或多个模块可以**共享一个数据库**.
由于ABP框架在所有方面都关心模块化,所以它为这个问题提供了**解决方案**. 如果你需要**自定义数据库结构**,那么应当了解这个解决方案.
@ -21,19 +25,21 @@ Entity Framework Core 提供了一种简单强大[数据库迁移系统](https:/
![bookstore-visual-studio-solution-v3](images/bookstore-visual-studio-solution-v3.png)
> 实际的解决方案结构可能会根据你的偏好有所不同,但是数据库部分是相同的.
实际的解决方案结构可能会根据你的偏好有所不同,但是数据库部分是相同的.
> 本文档将使用 `Acme.BookStore` 示例项目名称来引用项目和类. 你需要在解决方案中找到相应的类/项目.
### 数据库架构
### 数据库模式
启动模板已预安装了一些[应用程序模块](Modules/Index.md). 解决方案的每一层都有相应的模块包引用. 所以 `.EntityFrameworkCore` 项目含有使用 `EntityFrameworkCore` 模块的Nuget的引用:
![bookstore-efcore-dependencies](images/bookstore-efcore-dependencies.png)
通过这种方式,你可以看到所有的`.EntityFrameworkCore`项目下的EF Core的依赖.
通过这种方式,你可以看到所有的 `.EntityFrameworkCore` 项目下的**EF Core的依赖**.
> 除了模块引用之外,它还引用了 `Volo.Abp.EntityFrameworkCore.SqlServer` 包,因为启动模板预配置的是Sql Server. 参阅文档了解如何[切换到其它DBMS](Entity-Framework-Core-Other-DBMS.md).
虽然每个模块在设计上有自己的`DbContext`类,并且可以使用其自己的**物理数据库**,但解决方案的配置是使用**单个共享数据库**如下图所示:
虽然每个模块在设计上有自己的 `DbContext` 类,并且可以使用其自己的**物理数据库**,但解决方案的配置是使用**单个共享数据库**如下图所示:
![single-database-usage](images/single-database-usage.png)
@ -58,26 +64,26 @@ ABP框架的[连接字符串](Connection-Strings.md)系统允许你轻松为所
示例配置告诉ABP框架[审计日志模块](Modules/Audit-Logging.md)应使用第二个连接字符串.
然而这仅仅只是开始. 你还需要创建第二个数据库以及里面审计日志表并使用code frist的方法维护数据库表. 本文档的主要目的之一就是指导你了解这样的数据库分离场景.
**然而这仅仅只是开始**. 你还需要创建第二个数据库以及里面审计日志表并使用code frist迁移的方法维护数据库表. 本文档的主要目的之一就是指导你了解这样的数据库分离场景.
#### 模块表
每个模块都使用自己的数据库表. 例如[身份模块](Modules/Identity.md)有一些表来管理系统中的用户和角色.
每个模块都使用**自己的数据库表**. 例如[身份模块](Modules/Identity.md)有一些表来管理系统中的用户和角色.
#### 表前缀
##### 表前缀
由于所有模块都允许共享一个数据库(这是默认配置),所以模块通常使用前缀来对自己的表进行分组.
由于所有模块都允许共享一个数据库(这是默认配置),所以模块通常使用**表前缀**来对自己的表进行分组.
基础模块(如[身份](Modules/Identity.md), [租户管理](Modules/Tenant-Management.md) 和 [审计日志](Modules/Audit-Logging.md))使用 `Abp` 前缀, 其他的模块使用自己的前缀. 如[Identity Server](Modules/IdentityServer.md) 模块使用前缀 `IdentityServer`.
如果你愿意,你可以为你的应用程序的模块更改数据库表前缀.
如果你愿意,你可以为你的应用程序的模块**更改数据库表前缀**.
例:
````csharp
Volo.Abp.IdentityServer.AbpIdentityServerDbProperties.DbTablePrefix = "Ids";
````
这段代码更改了[Identity Server](Modules/IdentityServer.md)的前缀. 在应用程序的最开始编写这段代码.
这段代码更改了[Identity Server](Modules/IdentityServer.md)的前缀. 在应用程序的**最开始**编写这段代码.
> 每个模块还定义了 `DbSchema` 属性,你可以在支持schema的数据库中使用它.
@ -89,7 +95,7 @@ Volo.Abp.IdentityServer.AbpIdentityServerDbProperties.DbTablePrefix = "Ids";
这个项目有应用程序的 `DbContext`类(本例中的 `BookStoreDbContex` ).
每个模块都使用自己的 `DbContext` 类来访问数据库。同样你的应用程序有它自己的 `DbContext`. 通常在应用程序中使用这个 `DbContet`(如果你遵循最佳实践,应该在自定义[仓储](Repositories.md)中使用). 它几乎是一个空的 `DbContext`,因为你的应用程序在一开始没有任何实体,除了预定义的 `AppUser` 实体:
**每个模块都使用自己的 `DbContext`**来访问数据库。同样你的应用程序有它自己的 `DbContext`. 通常在应用程序中使用这个 `DbContet`(如果你遵循最佳实践,应该在自定义[仓储](Repositories.md)中使用). 它几乎是一个空的 `DbContext`,因为你的应用程序在一开始没有任何实体,除了预定义的 `AppUser` 实体:
````csharp
[ConnectionStringName("Default")]
@ -134,7 +140,7 @@ public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
* 它定义了一个 `[connectionStringName]` Attribute,它告诉ABP始终为此 `Dbcontext` 使用 `Default` 连接字符串.
* 它从 `AbpDbContext<T>` 而不是标准的 `DbContext` 类继承. 你可以参阅[EF Core集成](Entity-Framework-Core.md)文档了解更多. 现在你需要知道 `AbpDbContext<T>` 基类实现ABP框架的一些约定,为你自动化一些常见的任务.
* 它为 `AppUser` 实体定义了 `DbSet` 属性. `AppUser` 与[身份模块]的 `IdentityUser` 实体共享同一个表(默认名为 `AbpUsers`). 启动模板在应用程序中提供这个实体,因为我们认为用户实体一般需要应用程序中进行定制.
* 它为 `AppUser` 实体定义了 `DbSet` 属性. `AppUser` 与[身份模块](Modules/Identity.md)`IdentityUser` 实体共享同一个表(默认名为 `AbpUsers`). 启动模板在应用程序中提供这个实体,因为我们认为用户实体一般需要应用程序中进行定制.
* 构造函数接受一个 `DbContextOptions<T>` 实例.
* 它覆盖了 `OnModelCreating` 方法定义EF Core 映射.
* 首先调用 `base.OnModelCreating` 方法让ABP框架为我们实现基础映射.
@ -198,13 +204,11 @@ public class BookStoreMigrationsDbContext : AbpDbContext<BookStoreMigrationsDbCo
##### 共享映射代码
第一个问题是: 一个模块使用自己的 `DbContext` 这就需要到数据库的映射. 该 `MigrationsDbContext` 也需要相同的映射创建此模块的数据库表. 我们绝对不希望复制的映射代码.
第一个问题是: 一个模块使用自己的 `DbContext` 这就需要到数据库的映射. 该 `MigrationsDbContext` 也需要相同的映射创建此模块的数据库表. 我们绝对**不希望复制**的映射代码.
解决方案是定义一个扩展方法(在`ModelBuilder`)由两个 `DbContext` 类调用. 所以每个模块都定义了这样的扩展方法.
解决方案是定义一个**扩展方法**(在`ModelBuilder`)由两个 `DbContext` 类调用. 所以每个模块都定义了这样的扩展方法.
For example, the `builder.ConfigureBackgroundJobs()` method call configures the database tables for the [Background Jobs module](Modules/Background-Jobs.md). The definition of this extension method is something like that:
例如,`builder.ConfigureBackgroundJobs()` 方法调用[后台作业模块]配置数据库表. 扩展方法的定义如下:
例如 `builder.ConfigureBackgroundJobs()` 方法调用[后台作业模块](Modules/Background-Jobs.md)配置数据库表. 扩展方法的定义如下:
````csharp
public static class BackgroundJobsDbContextModelCreatingExtensions
@ -237,7 +241,7 @@ public static class BackgroundJobsDbContextModelCreatingExtensions
}
````
扩展方法还获取选项用于更改此模块的数据库表前缀和模式,但在这里并不重要.
u还获取选项用于更改此模块的数据库表前缀和模式,但在这里并不重要.
最终的应用程序在 `MigrationsDbContext` 类中调用扩展方法, 因此它可以确定此 `MigrationsDbContext` 维护的数据库中包含哪些模块. 如果要创建第二个数据库并将某些模块表移动到第二个数据库,则需要有第二个`MigrationsDbContext` 类,该类仅调用相关模块的扩展方法. 下一部分将详细介绍该主题.
@ -266,14 +270,14 @@ public class BackgroundJobsDbContext
}
````
这种方式,可以在 `DbContext` 类之间共享模块的映射配置.
通过这种方式,可以在 `DbContext` 类之间共享模块的映射配置. 上面的代码在相关的模块NuGet包中,所以你不需要关心它.
##### 重用模块的表
您可能想在应用程序中重用依赖模块的表. 在这种情况下你有两个选择:
您可能想在应用程序中**重用依赖模块的表**. 在这种情况下你有两个选择:
1. 你可以直接使用模块定义的实体.
2. 你可以创建一个新的实体映射到同一个数据库表。
1. 你可以**直接使用模块定义的实体**.
2. 你可以**创建一个新的实体**映射到同一个数据库表。
###### 使用由模块定义的实体
@ -309,12 +313,618 @@ namespace Acme.BookStore
示例注入了 `IRepository<IdentityUser,Guid>`(默认仓储). 它定义了标准的存储库方法并实现了 `IQueryable` 接口.
另外,身份模块定义了 `IIdentityUserRepository`(自定义仓储), 你的应用程序也可以注入和使用它. `IIdentityUserRepository``IdentityUser` 实体提供了额外的定制方法,但它没有实现 `IQueryable`.
另外,身份模块定义了 `IIdentityUserRepository`(自定义仓储),你的应用程序也可以注入和使用它. `IIdentityUserRepository``IdentityUser` 实体提供了额外的定制方法,但它没有实现 `IQueryable`.
###### 创建一个新的实体
TODO
如果你只是按原样使用实体,那么使用模块的实体是很容易的. 但是在下面的情况下你可能需要定义自己的实体并映射到模块实体相同的数据库表.
* 你想要**添加一个新的字段**到表中并将其映射到实体的属性. 你无法使用模块的实体,因为它没有相关的属性.
* 你想要**使用的表字段的一个子集**. 你不希望访问实体的所有属性并隐藏不相关的属性(从安全的角度或仅出于设计目的).
* 你不希望直接**依赖于**模块实体类.
无论哪一种情况,流程都是一样的. 假如你想要创建一个名为 `AppRole` 的实体,将其映射到与[身份模块](Modules/Identity.md)的 `IdentityRole` 实体相同的表
在这里我们将介绍实现方式, 然后**将讨论这种方法的局限性**.
首先,在你的 `.Domain` 项目一个新的 `AppRole` 类:
````csharp
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Acme.BookStore.Roles
{
public class AppRole : AggregateRoot<Guid>, IMultiTenant
{
// Properties shared with the IdentityRole class
public Guid? TenantId { get; private set; }
public string Name { get; private set; }
//Additional properties
public string Title { get; set; }
private AppRole()
{
}
}
}
````
* 它继承了[`AggregateRoot<Guid>`类](Entities.md)和实现了[`IMultiTenant`]接口(Multi-Tenancy.md),因为 `IdentityRole` 也做了同样的继承.
* 你可以添加 `IdentityRole` 实体定义的任何属性. 本例只加了 `TenantId``Name` 属性,因为我们这里只需要它们. 你可以把setters设置为私有(如同本例)以防意外更改身份模块的属性.
* 你可以添加自定义(附加)属性. 本例添加了 `Title` 属性.
* **构造函数是私有的**,所以它不允许直接创建一个新的 `AppRole` 实体。创建角色身份模块的责任. 你可以查询角色,设置/更新自定义属性,但做为最佳实践你不应该在代码中创建和删除角色(尽管没有强制的限制).
现在是时候定义EF Core映射. 打开应用程序的 `DbContext` (此示例中是 `BookStoreDbContext` )添加以下属性:
````csharp
public DbSet<AppRole> Roles { get; set; }
````
然后在 `OnModelCreating` 方法中配置映射(调用 `base.OnModelCreating(builder)` 之后):
````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Configure the shared tables (with included modules) here */
//CONFIGURE THE AppRole ENTITY
builder.Entity<AppRole>(b =>
{
b.ToTable("AbpRoles");
b.ConfigureByConvention();
b.ConfigureCustomRoleProperties();
});
...
/* Configure your own tables/entities inside the ConfigureBookStore method */
builder.ConfigureBookStore();
}
````
我们添加了以下几行:
````csharp
builder.Entity<AppRole>(b =>
{
b.ToTable("AbpRoles");
b.ConfigureByConvention();
b.ConfigureCustomRoleProperties();
});
````
* 它映射到 `AbpRoles` 表,与 `IdentityRole` 实体共享.
* `ConfigureByConvention()` 配置了标准/基本属性(像`TenantId`),建议总是调用它.
`ConfigureCustomRoleProperties()` 还不存在. 在 `BookStoreDbContextModelCreatingExtensions` 类中定义它 (在 `.EntityFrameworkCore` 项目的 `DbContext` 附近):
````csharp
public static void ConfigureCustomRoleProperties<TRole>(this EntityTypeBuilder<TRole> b)
where TRole : class, IEntity<Guid>
{
b.Property<string>(nameof(AppRole.Title)).HasMaxLength(128);
}
````
* 这个方法只定义实体的**自定义属性**.
* 遗憾的是,我们不能在这里充分的利用**类型安全**(通过引用`AppRole`实体). 我们能做的最好就是使用 `Title` 名称做为类型安全。
你已经为运行应用程序使用的 `DbContext` 配置了自定义属性. 我们还需要配置 `MigrationsDbContext`.
打开`MigrationsDbContext`(本例是 `BookStoreMigrationsDbContext`)进行以下更改:
````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
...
/* Configure customizations for entities from the modules included */
//CONFIGURE THE CUSTOM ROLE PROPERTIES
builder.Entity<IdentityRole>(b =>
{
b.ConfigureCustomRoleProperties();
});
...
/* Configure your own tables/entities inside the ConfigureBookStore method */
builder.ConfigureBookStore();
}
````
只增加下面几行:
````csharp
builder.Entity<IdentityRole>(b =>
{
b.ConfigureCustomRoleProperties();
});
````
通过这种方式,我们重用了用于为角色配置自定义属性映射的扩展方法. 但是对 `IdentityRole` 实体进行了相同的自定义.
现在你可以在包管理控制台(记得选择 `.EntityFrameworkCore.DbMigrations` 做为PMC的默认项目并将 `.Web` 项目设置为启动项目)使用标准的 `Add-Migration` 命令添加一个新的EF Core数据库迁移.
![pmc-add-migration-role-title](images/pmc-add-migration-role-title.png)
命令将创建一个新的Code First迁移类,如下所示:
````csharp
public partial class Added_Title_To_Roles : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Title",
table: "AbpRoles",
maxLength: 128,
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Title",
table: "AbpRoles");
}
}
````
全部完成,只运行在PMC运行`Update-Database` 命令或运行你解决方案中的`.DbMigrator` 项目应用数据库更改.
现在你可以像使用应用程序的其他实体一样使用 `AppRole` 实体. 下面是查询和更新角色的[应用服务](Application-Services.md).
示例:
````csharp
public class AppRoleAppService : ApplicationService, IAppRoleAppService
{
private readonly IRepository<AppRole, Guid> _appRoleRepository;
public AppRoleAppService(IRepository<AppRole, Guid> appRoleRepository)
{
_appRoleRepository = appRoleRepository;
}
public async Task<List<AppRoleDto>> GetListAsync()
{
var roles = await _appRoleRepository.GetListAsync();
return roles
.Select(r => new AppRoleDto
{
Id = r.Id,
Name = r.Name,
Title = r.Title
})
.ToList();
}
public async Task UpdateTitleAsync(Guid id, string title)
{
var role = await _appRoleRepository.GetAsync(id);
role.Title = title;
await _appRoleRepository.UpdateAsync(role);
}
}
````
创建新实体并将其映射到依赖模块的表存在一些**限制**:
* 你的**自定义属性必须是可空的**. 例如 `AppRole.Title` 在这里是可空的. 否则身份模块会抛出异常,因为它在向数据库插入新角色时不知道如何如何填充标题.
* 好的做法是,你不应更新**模块定义的属性**,尤其是在需要业务逻辑的情况下. 通常你可以管理自己(自定义)的属性.
##### 替代方法
你可以使用以下方法替代创建实体添加自定义属性.
###### 使用ExtraProperties
所有从 `AggregateRoot` 派生的实体都可以在 `ExtraProperties` 属性中存储键值对, 它是 `Dictionary<string, object>` 类型在数据库中被序列化为JSON. 所以你可以在字典中添加值用于查询,无需更改实体.
例如你可以将查询属性 `Title` 存储在 `IdentityRole` 中,而不是创建一个新的实体.
例:
````csharp
public class IdentityRoleExtendingService : ITransientDependency
{
private readonly IIdentityRoleRepository _identityRoleRepository;
public IdentityRoleExtendingService(IIdentityRoleRepository identityRoleRepository)
{
_identityRoleRepository = identityRoleRepository;
}
public async Task<string> GetTitleAsync(Guid id)
{
var role = await _identityRoleRepository.GetAsync(id);
return role.GetProperty<string>("Title");
}
public async Task SetTitleAsync(Guid id, string newTitle)
{
var role = await _identityRoleRepository.GetAsync(id);
role.SetProperty("Title", newTitle);
await _identityRoleRepository.UpdateAsync(role);
}
}
````
* `GetProperty``SetProperty` 方法是在 `role.ExtraProperties` 词典中获取和设置值的简化写法,推荐使用.
通过这种方式你可以轻松任何类型的值附加到依赖模块的实体. 不过这种方法也有一些缺点:
* 所有的额外属性都存储在数据库中的一个**JSON对象**,它们不是作为表的字段存储,与简单的表字段相比创建索引和针对此属性使用SQL查询将更加困难.
* 属性名称是字符串,他们**不是类型安全的**. 建议这些类型的属性定义常量,以防止拼写错误.
###### 创建新表
你可以创建**自己的表**来存储属性,而不是创建新实体并映射到同一表. 你通常复制原始实体的一些值. 例如可以将 `Name` 字段添加到你自己的表中,它是原表中 `Name` 字段的副本.
在这种情况下你不需要处理迁移问题,但是需要处理数据复制问题. 当重复的值发生变化时,你应该在表中同步相同的变化. 你可以使用本地或分布式[事件总线](Event-Bus.md)订阅原始实体的更改事件. 这是根据来自另一个微服务的数据推荐的方法,特别是如果它们有单独的物理数据库(你可以在网络中搜索关于微服务设计的数据共享,这是一个广泛的主题).
#### 讨论另一种场景:每个模块管理自己的迁移路径
上面提到, `.EntityFrameworkCore.DbMigrations` 合并所有模块的所有数据库映射(加上应用程序映射)创建统一的迁移路径.
另一种方法是允许每个模块拥有自己的迁移来维护其数据库表. 虽然它在一开始看起来更模块化,但它有一些重要的缺点:
* **EF Core迁移系统依赖于数据库DBMS提供程序**. 例如,如果一个模块为SQL Server创建迁移,那么你就不能使用MySQL使用这个迁移代码,对于模块而言为所有可用的DBMS提供程序维护迁移是不切实际的. 将迁移留给应用程序代码(如本文档中所解释的),你就可以在**应用程序代码中选择DBMS**.
* 在模块之间 **共享表** 或在应用程序中 **重用模块的表** 会更加困难. 因为EF Core迁移系统不能处理它,并会抛出异常,例如"数据库中已存在表XXX".
* **自定义/增强** 映射和生成迁移代码会更加困难.
* 当你使用多个模块时更难跟踪和**应用数据库更改**.
## 使用多个数据库
默认的启动模板被组织为所有模块和应用程序使用单个数据库. 但是ABP框架和所有预构建的模块都被设计为**可以使用多个数据库**. 每个模块可以使用自己的数据库,也可以将模块分组到几个数据库中.
本节将说明如何将审计日志,设置管理和权限管理模块表移至**第二个数据库**,而其余模块继续使用主("Default")数据库.
结构如下图所示:
![single-database-usage](images/multiple-database-usage.png)
### 更改连接字符串部分
第一步是更改所有 `appsettings.json` 文件的连接字符串部分. 最初它是这样的:
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true"
}
````
更改如下所示:
````json
"ConnectionStrings": {
"Default": "Server=localhost;Database=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true",
"AbpPermissionManagement": "Server=localhost;Database=BookStore_SecondDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"AbpSettingManagement": "Server=localhost;Database=BookStore_SecondDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"AbpAuditLogging": "Server=localhost;Database=BookStore_SecondDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
````
为相关模块添加了**另外三个连接字符串**,以针对 `BookStore_SecondDb` 数据库(它们都是相同的). 例如 `AbpPermissionManagement` 是权限管理模块的连接字符串.
`AbpPermissionManagement` 是权限管理模块[定义](https://github.com/abpframework/abp/blob/97eaa6ff5a044f503465455c86332e5a277b077a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDbProperties.cs#L11)的一个常量. 如果定义值,ABP框架[连接字符串选择系统](Connection-Strings.md)将为权限管理模块选择此连接字符串. 如果不定义它将回退到 `Default` 连接字符串.
### 创建第二个迁移项目
在**运行时**上定义如上所述的连接字符串就足够了. 但是 `BookStore_SecondDb` 数据库还不存在. 你需要为相关模块创建数据库表.
就像主数据库我们要使用的EF Core Code Frist迁移系统创建和维护第二个数据库.
一种简单的方法是为第二个迁移 `DbContext` 创建第二个项目(`.csproj`).
在你的解决方案中创建一个名为 `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb` 的**类库项目**(如果不喜欢你可以进行其他命名).
`.csproj` 内容应该和以下类似:
````xml
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Acme.BookStore.DbMigrationsForSecondDb</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Acme.BookStore.EntityFrameworkCore\Acme.BookStore.EntityFrameworkCore.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0" />
</ItemGroup>
</Project>
````
你可以仅复制和修改原始 `.DbMigrations` 项目的内容. 该项目引用 `.EntityFrameworkCore` 项目. **唯一的区别**是 `RootNamespace` 值.
`.Web` 项目中添加对该项目的引用(否则,EF Core Tool不允许使用`Add-migration`命令).
### 创建第二个DbMigrationDbContext
为迁移创建一个新的 `DbContext` 并调用模块的**扩展方法**为相关模块配置数据库表:
````csharp
[ConnectionStringName("AbpPermissionManagement")]
public class BookStoreSecondMigrationsDbContext :
AbpDbContext<BookStoreSecondMigrationsDbContext>
{
public BookStoreSecondMigrationsDbContext(
DbContextOptions<BookStoreSecondMigrationsDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureAuditLogging();
}
}
````
> `[ConnectionStringName(...)` Attribute 在这里非常重,它告诉ABP框架应该为这个 `DbContext` 使用哪个连接字符串. 我们使用了 `AbpPermissionManagement`.
创建一个**Design Time Db Factory**类, EF Core Tool需要使用它(例如`Add-Migration`和`Update-Database` PCM命令):
````csharp
/* This class is needed for EF Core console commands
* (like Add-Migration and Update-Database commands) */
public class BookStoreSecondMigrationsDbContextFactory
: IDesignTimeDbContextFactory<BookStoreSecondMigrationsDbContext>
{
public BookStoreSecondMigrationsDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<BookStoreSecondMigrationsDbContext>()
.UseSqlServer(configuration.GetConnectionString("AbpPermissionManagement"));
return new BookStoreSecondMigrationsDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
````
它与 `.EntityFrameworCore.DbMigrations` 项目中的类相似, 除了它使用 `AbpPermissionManagement` 连接字符串.
现在你可以打开包管理器控制台, 选择 `.EntityFrameworkCore.DbMigrationsForSecondDb` 项目为默认项目(确保`.Web`项目是启动项目),运行 `Add-Migration "Initial``Update-Database` 命令,如下所示:
![pmc-add-migration-initial-update-database](images/pmc-add-migration-initial-update-database.png)
现在你应该有一个包含相关数据库表的新数据库.
![bookstore-second-database](images/bookstore-second-database.png)
### 从主数据库中删除模块
我们 **创建第二个数据库** 包含了审计日志,权限管理和设置管理模块表. 我们应该从**主数据库中删除这些表**,这是很容易的.
S
首先移除从 `MigrationsDbContext` 类以下行(本例中是`BookStoreMigrationsDbContext`):
````csharp
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureAuditLogging();
````
打开包管理器控制台, 选择 `.EntityFrameworkCore.DbMigrations` 为默认的项目(确保`.Web`项目是启动项目)运行以下命令:
````
Add-Migration "Removed_Audit_Setting_Permission_Modules"
````
此命令将创建一个如下所示的迁移类:
````csharp
public partial class Removed_Audit_Setting_Permission_Modules : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpAuditLogActions");
migrationBuilder.DropTable(
name: "AbpEntityPropertyChanges");
migrationBuilder.DropTable(
name: "AbpPermissionGrants");
migrationBuilder.DropTable(
name: "AbpSettings");
migrationBuilder.DropTable(
name: "AbpEntityChanges");
migrationBuilder.DropTable(
name: "AbpAuditLogs");
}
...
}
````
在这一步需要小心:
* 如果你是 **已经在生产的系统**, 那么你应该关心**数据丢失**,在删除表之前需要将表的内容移动到第二个数据库.
* 如果你还 **没有开始** 你的项目, 你可以考虑 **删除所有的迁移** 并且重新生成初始版本,让迁移历史更加清晰.
运行 `Update-Database` 命令从主数据库中删除表.
请注意如果你还没有将其复制到新数据库,那么还会**删除了一些初始种子数据**(例如授予管理员角色的权限). 如果你运行应用程序可能无法再登录. 解决方案很简单:在解决方案中重新运行 `.DbMigrator` 控制台应用程序,它会为新数据库添加种子数据.
### 自动化第二个数据库模式迁移
`.DbMigrator` 控制台应用程序可以跨多个数据库运行数据库种子代码无需任何其他配置. 但它不能在第二个数据库迁移项目中运行EF Core First迁移. 下面介绍如何配置控制台迁移应用程序以处理两个数据库.
#### 实现 IBookStoreDbSchemaMigrator
`Acme.BookStore.EntityFrameworkCore.DbMigrations` 项目中的 `EntityFrameworkCoreBookStoreDbSchemaMigrator` 类负责为 `BookStoreMigrationsDbContext` 迁移数据库模式. 它应该是这样的:
````csharp
[Dependency(ReplaceServices = true)]
public class EntityFrameworkCoreBookStoreDbSchemaMigrator
: IBookStoreDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreBookStoreDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We are intentionally resolving the BookStoreMigrationsDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<BookStoreMigrationsDbContext>()
.Database
.MigrateAsync();
}
}
````
它实现了 `IBookStoreDbSchemaMigrator` 并**替换现有服务**(见第一行).
删除 `[Dependency(ReplaceServices = true)]`,因为我们有这个接口的两个实现并且希望同时使用它们.
在新的迁移项目(`Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb`)创建它的一个副本,内部使用 `BookStoreSecondMigrationsDbContext` . 示例实现:
````csharp
public class EntityFrameworkCoreSecondBookStoreDbSchemaMigrator
: IBookStoreDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreSecondBookStoreDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We are intentionally resolving the BookStoreSecondMigrationsDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<BookStoreSecondMigrationsDbContext>()
.Database
.MigrateAsync();
}
}
````
> 类的名称对于[依赖注入](Dependency-Injection.md)非常重要. 为了可以使用 `IBookStoreDbSchemaMigrator` 引用注入它的结尾应该是 `BookStoreDbSchemaMigrator`.
现在我们有 `IBookStoreDbSchemaMigrator` 接口的两个实现,每个实现负责迁移相关的数据库模式.
#### 为第二个迁移项目定义一个模块类
是时候为第二次迁移(`Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb`)项目定义[模块](Module-Development-Basics.md)类了:
````csharp
[DependsOn(
typeof(BookStoreEntityFrameworkCoreModule)
)]
public class BookStoreEntityFrameworkCoreSecondDbMigrationsModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<BookStoreSecondMigrationsDbContext>();
}
}
````
`Acme.BookStore.DbMigrator` 项目中引用 `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSecondDb` 项目并且将 `typeof(BookStoreEntityFrameworkCoreSecondDbMigrationsModule)` 依赖项添加到 `BookStoreDbMigratorModule` 的依赖项列表中. `BookStoreDbMigratorModule` 类应该是这样的:
````csharp
[DependsOn(
typeof(AbpAutofacModule),
typeof(BookStoreEntityFrameworkCoreDbMigrationsModule),
typeof(BookStoreEntityFrameworkCoreSecondDbMigrationsModule), // ADDED THIS!
typeof(BookStoreApplicationContractsModule)
)]
public class BookStoreDbMigratorModule : AbpModule
{
...
}
````
#### BookStoreDbMigrationService
最后还需要找到 `Acme.BookStore.Domain` 项目的 `BookStoreDbMigrationService` 类,它当前被设计为使用单个 `IBookStoreDbSchemaMigrator` 实现,但现在我们有了两个.
它注入了 `IBookStoreDbSchemaMigrator`. 使用 `IEnumerable<IBookStoreDbSchemaMigrator>` 注入替换它, ([依赖注入系统](Dependency-Injection.md) 允许像这样注入接口的多个实现).
现在你有 **模式迁移器的集合** ,找到以下行:
````csharp
await _dbSchemaMigrators.MigrateAsync();
````
将这行代码进行以下改变:
````csharp
foreach (var migrator in _dbSchemaMigrators)
{
await migrator.MigrateAsync();
}
````
你可以运行 `.DbMigrator` 应用程序应用迁移和初始化种子数据. 你可以删除这两个数据库进行测试,然后再次运行 `.DbMigrator` 应用程序,它会创建两个数据库.
##### 讨论另一种场景:每个模块管理自己的迁移路径
## 结论
TODO
本文档说明了如何拆分数据库以及管理Entity Framework Core解决方案的数据库迁移. 简而言之,你需要为每个不同的数据库创建一个单独的迁移项目.

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/Part-I.md → docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/Part-II.md → docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/Part-III.md → docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-appservice-tests.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-book-list-2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list-2.png

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-book-list.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-books-table-actions.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-books-table.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table.png

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-create-dialog-2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog-2.png

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-create-dialog.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog.png

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-create-template.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-homepage.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-homepage.png

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-localization-files-v2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-menu-items.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-new-book-button.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-swagger.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-test-projects-v2.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-user-management.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-user-management.png

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png → docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

0
docs/zh-Hans/Tutorials/AspNetCore-Mvc/Index.md → docs/zh-Hans/Tutorials/Index.md

BIN
docs/zh-Hans/_resources/Diagrams.docx

Binary file not shown.

5
docs/zh-Hans/docs-nav.json

@ -278,6 +278,11 @@
"path": "Entity-Framework-Core.md",
"items": [
{
"text": "数据库迁移",
"path": "Entity-Framework-Core-Migrations.md"
},
{
"text": "切换DMBS",
"path": "Entity-Framework-Core-Other-DBMS.md",
"items":[

BIN
docs/zh-Hans/images/bookstore-second-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
docs/zh-Hans/images/multiple-database-usage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/zh-Hans/images/pmc-add-migration-initial-update-database.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/zh-Hans/images/pmc-add-migration-role-title.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

21
framework/Volo.Abp.sln

@ -263,6 +263,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Serilog
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.IdentityModel.Relay", "src\Volo.Abp.Http.Client.IdentityModel.Relay\Volo.Abp.Http.Client.IdentityModel.Relay.csproj", "{925AF101-2203-409C-9C3B-03917316858F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BackgroundJobs.Quartz", "src\Volo.Abp.BackgroundJobs.Quartz\Volo.Abp.BackgroundJobs.Quartz.csproj", "{2307198B-5837-4F05-AA84-D6EC2A923D69}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Quartz", "src\Volo.Abp.Quartz\Volo.Abp.Quartz.csproj", "{9467418B-4A9B-4093-9B31-01A9DEF5B372}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundWorkers.Quartz", "src\Volo.Abp.BackgroundWorkers.Quartz\Volo.Abp.BackgroundWorkers.Quartz.csproj", "{CD5770BB-2E0C-4B3C-80E0-21B8CC43DBA9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo", "src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj", "{29E42ADB-85F8-44AE-A9B0-078F84C1B866}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo\Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.csproj", "{0C498CF2-D052-4BF7-AD35-509A90F69707}"
@ -785,6 +791,18 @@ Global
{925AF101-2203-409C-9C3B-03917316858F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{925AF101-2203-409C-9C3B-03917316858F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{925AF101-2203-409C-9C3B-03917316858F}.Release|Any CPU.Build.0 = Release|Any CPU
{2307198B-5837-4F05-AA84-D6EC2A923D69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2307198B-5837-4F05-AA84-D6EC2A923D69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2307198B-5837-4F05-AA84-D6EC2A923D69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2307198B-5837-4F05-AA84-D6EC2A923D69}.Release|Any CPU.Build.0 = Release|Any CPU
{9467418B-4A9B-4093-9B31-01A9DEF5B372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9467418B-4A9B-4093-9B31-01A9DEF5B372}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9467418B-4A9B-4093-9B31-01A9DEF5B372}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9467418B-4A9B-4093-9B31-01A9DEF5B372}.Release|Any CPU.Build.0 = Release|Any CPU
{CD5770BB-2E0C-4B3C-80E0-21B8CC43DBA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD5770BB-2E0C-4B3C-80E0-21B8CC43DBA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD5770BB-2E0C-4B3C-80E0-21B8CC43DBA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD5770BB-2E0C-4B3C-80E0-21B8CC43DBA9}.Release|Any CPU.Build.0 = Release|Any CPU
{29E42ADB-85F8-44AE-A9B0-078F84C1B866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29E42ADB-85F8-44AE-A9B0-078F84C1B866}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29E42ADB-85F8-44AE-A9B0-078F84C1B866}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -926,6 +944,9 @@ Global
{3B801003-BE74-49ED-9749-DA5E99F45EBF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{9CAA07ED-FE5C-4427-A6FA-6C6CB5B4CC62} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{925AF101-2203-409C-9C3B-03917316858F} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{2307198B-5837-4F05-AA84-D6EC2A923D69} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{9467418B-4A9B-4093-9B31-01A9DEF5B372} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{CD5770BB-2E0C-4B3C-80E0-21B8CC43DBA9} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{29E42ADB-85F8-44AE-A9B0-078F84C1B866} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0C498CF2-D052-4BF7-AD35-509A90F69707} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/AlertsDemo/AlertsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.AlertsDemo
{
[Widget]
public class AlertsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/AlertsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

70
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/AlertsDemo/Default.cshtml

@ -0,0 +1,70 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.AlertsDemo
<abp-component-demo-section title="Examples" view-path="@AlertsDemoViewComponent.ViewPath">
<abp-alert alert-type="Primary">
A simple primary alert—check it out!
</abp-alert>
<abp-alert alert-type="Secondary">
A simple secondary alert—check it out!
</abp-alert>
<abp-alert alert-type="Success">
A simple success alert—check it out!
</abp-alert>
<abp-alert alert-type="Danger">
A simple danger alert—check it out!
</abp-alert>
<abp-alert alert-type="Warning">
A simple warning alert—check it out!
</abp-alert>
<abp-alert alert-type="Info">
A simple info alert—check it out!
</abp-alert>
<abp-alert alert-type="Light">
A simple light alert—check it out!
</abp-alert>
<abp-alert alert-type="Dark">
A simple dark alert—check it out!
</abp-alert>
</abp-component-demo-section>
<abp-component-demo-section title="Link color" view-path="@AlertsDemoViewComponent.ViewPath">
<abp-alert alert-type="Primary">
A simple primary alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Secondary">
A simple secondary alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Success">
A simple success alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Danger">
A simple danger alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Warning">
A simple warning alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Info">
A simple info alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Light">
A simple light alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
<abp-alert alert-type="Dark">
A simple dark alert with <a abp-alert-link href="#">an example link</a>. Give it a click if you like.
</abp-alert>
</abp-component-demo-section>
<abp-component-demo-section title="Additional content" view-path="@AlertsDemoViewComponent.ViewPath">
<abp-alert alert-type="Success">
<h4>Well done!</h4>
<p>Aww yeah, you successfully read this important alert message. This example text is going to run a bit longer so that you can see how spacing within an alert works with this kind of content.</p>
<hr>
<p class="mb-0">Whenever you need to, be sure to use margin utilities to keep things nice and tidy.</p>
</abp-alert>
</abp-component-demo-section>
<abp-component-demo-section title="Dismissing" view-path="@AlertsDemoViewComponent.ViewPath">
<abp-alert alert-type="Warning" dismissible="true">
Holy guacamole! You should check in on some of those fields below.
</abp-alert>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BadgesDemo/BadgesDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.BadgesDemo
{
[Widget]
public class BadgesDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/BadgesDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

49
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BadgesDemo/Default.cshtml

@ -0,0 +1,49 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.BadgesDemo
<abp-component-demo-section title="Example" view-path="@BadgesDemoViewComponent.ViewPath">
<h1>Example heading <span abp-badge="Secondary">New</span></h1>
<h2>Example heading <span abp-badge="Secondary">New</span></h2>
<h3>Example heading <span abp-badge="Secondary">New</span></h3>
<h4>Example heading <span abp-badge="Secondary">New</span></h4>
<h5>Example heading <span abp-badge="Secondary">New</span></h5>
<h6>Example heading <span abp-badge="Secondary">New</span></h6>
</abp-component-demo-section>
<abp-component-demo-section title="Notifications" view-path="@BadgesDemoViewComponent.ViewPath">
<abp-button button-type="Primary">
Notifications <span abp-badge="Light">4</span>
</abp-button>
</abp-component-demo-section>
<abp-component-demo-section title="Contextual variations" view-path="@BadgesDemoViewComponent.ViewPath">
<span abp-badge="Primary">Primary</span>
<span abp-badge="Secondary">Secondary</span>
<span abp-badge="Success">Success</span>
<span abp-badge="Danger">Danger</span>
<span abp-badge="Warning">Warning</span>
<span abp-badge="Info">Info</span>
<span abp-badge="Light">Light</span>
<span abp-badge="Dark">Dark</span>
</abp-component-demo-section>
<abp-component-demo-section title="Pill badges" view-path="@BadgesDemoViewComponent.ViewPath">
<span abp-badge-pill="Primary">Primary</span>
<span abp-badge-pill="Secondary">Secondary</span>
<span abp-badge-pill="Success">Success</span>
<span abp-badge-pill="Danger">Danger</span>
<span abp-badge-pill="Warning">Warning</span>
<span abp-badge-pill="Info">Info</span>
<span abp-badge-pill="Light">Light</span>
<span abp-badge-pill="Dark">Dark</span>
</abp-component-demo-section>
<abp-component-demo-section title="Links" view-path="@BadgesDemoViewComponent.ViewPath">
<a href="#" abp-badge="Primary">Primary</a>
<a href="#" abp-badge="Secondary">Secondary</a>
<a href="#" abp-badge="Success">Success</a>
<a href="#" abp-badge="Danger">Danger</a>
<a href="#" abp-badge="Warning">Warning</a>
<a href="#" abp-badge="Info">Info</a>
<a href="#" abp-badge="Light">Light</a>
<a href="#" abp-badge="Dark">Dark</a>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BordersDemo/BordersDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.BordersDemo
{
[Widget]
public class BordersDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/BordersDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

46
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BordersDemo/Default.cshtml

@ -0,0 +1,46 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.BordersDemo
<abp-component-demo-section title="Additive" view-path="@BordersDemoViewComponent.ViewPath">
<span abp-border="Default"></span>
<span abp-border="Top"></span>
<span abp-border="Right"></span>
<span abp-border="Bottom"></span>
<span abp-border="Left"></span>
</abp-component-demo-section>
<abp-component-demo-section title="Subtractive" view-path="@BordersDemoViewComponent.ViewPath">
<span abp-border="_0"></span>
<span abp-border="Top_0"></span>
<span abp-border="Right_0"></span>
<span abp-border="Bottom_0"></span>
<span abp-border="Left_0"></span>
</abp-component-demo-section>
<abp-component-demo-section title="Border color" view-path="@BordersDemoViewComponent.ViewPath">
<span abp-border="Primary"></span>
<span abp-border="Secondary"></span>
<span abp-border="Success"></span>
<span abp-border="Danger"></span>
<span abp-border="Info"></span>
<span abp-border="Light"></span>
<span abp-border="Dark"></span>
<span abp-border="White"></span>
<br />
<span abp-border="Left_Primary"></span>
<span abp-border="Top_Secondary"></span>
<span abp-border="Right_Success"></span>
<span abp-border="Bottom_Danger"></span>
<span abp-border="Bottom_Warning"></span>
<span abp-border="Left_Info"></span>
<span abp-border="Top_Light"></span>
<span abp-border="Right_Dark"></span>
</abp-component-demo-section>
<abp-component-demo-section title="Border-radius" view-path="@BordersDemoViewComponent.ViewPath">
<span abp-border="Primary" abp-rounded="Default"></span>
<span abp-border="Primary" abp-rounded="_0"></span>
<span abp-border="Primary" abp-rounded="Top"></span>
<span abp-border="Primary" abp-rounded="Left"></span>
<span abp-border="Primary" abp-rounded="Bottom"></span>
<span abp-border="Primary" abp-rounded="Right"></span>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BreadcrumbsDemo/BreadcrumbsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.BreadcrumbsDemo
{
[Widget]
public class BreadcrumbsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/BreadcrumbsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

18
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/BreadcrumbsDemo/Default.cshtml

@ -0,0 +1,18 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.BreadcrumbsDemo
<abp-component-demo-section title="Example" view-path="@BreadcrumbsDemoViewComponent.ViewPath">
<abp-breadcrumb>
<abp-breadcrumb-item title="Home" />
</abp-breadcrumb>
<abp-breadcrumb>
<abp-breadcrumb-item href="#" title="Home" />
<abp-breadcrumb-item title="Library" />
</abp-breadcrumb>
<abp-breadcrumb>
<abp-breadcrumb-item href="#" title="Home" />
<abp-breadcrumb-item href="#" title="Library" />
<abp-breadcrumb-item title="Page" />
</abp-breadcrumb>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ButtonGroupsDemo/ButtonGroupsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ButtonGroupsDemo
{
[Widget]
public class ButtonGroupsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/ButtonGroupsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

68
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ButtonGroupsDemo/Default.cshtml

@ -0,0 +1,68 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ButtonGroupsDemo
<abp-component-demo-section title="Basic example" view-path="@ButtonGroupsDemoViewComponent.ViewPath">
<abp-button-group>
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>
<abp-button button-type="Secondary">Right</abp-button>
</abp-button-group>
</abp-component-demo-section>
<abp-component-demo-section title="Button Toolbar" view-path="@ButtonGroupsDemoViewComponent.ViewPath">
<abp-button-toolbar>
<abp-button-group class="mr-2">
<abp-button button-type="Secondary">1</abp-button>
<abp-button button-type="Secondary">2</abp-button>
<abp-button button-type="Secondary">3</abp-button>
<abp-button button-type="Secondary">4</abp-button>
</abp-button-group>
<abp-button-group class="mr-2">
<abp-button button-type="Secondary">5</abp-button>
<abp-button button-type="Secondary">6</abp-button>
<abp-button button-type="Secondary">7</abp-button>
</abp-button-group>
<abp-button-group>
<abp-button button-type="Secondary">8</abp-button>
</abp-button-group>
</abp-button-toolbar>
</abp-component-demo-section>
<abp-component-demo-section title="Sizing" view-path="@ButtonGroupsDemoViewComponent.ViewPath">
<abp-button-group size="Large">
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>
<abp-button button-type="Secondary">Right</abp-button>
</abp-button-group>
<abp-button-group>
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>
<abp-button button-type="Secondary">Right</abp-button>
</abp-button-group>
<abp-button-group size="Small">
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>
<abp-button button-type="Secondary">Right</abp-button>
</abp-button-group>
</abp-component-demo-section>
<abp-component-demo-section title="Nesting" view-path="@ButtonGroupsDemoViewComponent.ViewPath">
<abp-button-group>
<abp-button button-type="Secondary">1</abp-button>
<abp-button button-type="Secondary">2</abp-button>
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Dropdown" />
<abp-dropdown-menu>
<abp-dropdown-item href="#"> Dropdown link </abp-dropdown-item>
<abp-dropdown-item href="#"> Dropdown link </abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-button-group>
</abp-component-demo-section>
<abp-component-demo-section title="Vertical variation" view-path="@ButtonGroupsDemoViewComponent.ViewPath">
<abp-button-group direction="Vertical">
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>
<abp-button button-type="Secondary">Right</abp-button>
</abp-button-group>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CardsDemo/CardsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.CardsDemo
{
[Widget]
public class CardsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/CardsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

156
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CardsDemo/Default.cshtml

@ -0,0 +1,156 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.CardsDemo
<abp-component-demo-section title="Example" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card style="width: 18rem;">
<img abp-card-image="Top" src="~/imgs/demo/300x200.png" />
<abp-card-body>
<abp-card-title>Card Title</abp-card-title>
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text>
<a abp-button="Primary" href="#"> Go somewhere</a>
</abp-card-body>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="Titles, text, and links" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card style="width: 18rem;">
<abp-card-body>
<abp-card-title>Card title</abp-card-title>
<abp-card-subtitle class="mb-2 text-muted">Card subtitle</abp-card-subtitle>
<abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text>
<a abp-card-link href="#">Card link</a>
<a abp-card-link href="#">Another link</a>
</abp-card-body>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="List groups" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card style="width: 18rem;">
<abp-list-group flush="true">
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="List Title" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card style="width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-list-group flush="true">
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="Kitchen sink" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card style="width: 18rem;">
<img abp-card-image="Top" src="~/imgs/demo/300x200.png" />
<abp-card-body>
<abp-card-title>Card Title</abp-card-title><abp-card-text>Some quick example text to build on the card title and make up the bulk of the card's content.</abp-card-text>
</abp-card-body>
<abp-list-group flush="true">
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
<abp-card-body>
<a abp-card-link href="#">Card link</a>
<a abp-card-link href="#">Another link</a>
</abp-card-body>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="Header and footer" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card style="width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
<a abp-button="Primary" href="#"> Go somewhere</a>
</abp-card-body>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="Quoate" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card>
<abp-card-header>Quote</abp-card-header>
<abp-card-body>
<abp-blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p>
<footer>Someone famous in Source Title</footer>
</abp-blockquote>
</abp-card-body>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="Featured" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card class="text-center">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
<a abp-button="Primary" href="#"> Go somewhere</a>
</abp-card-body>
<abp-card-footer class="text-muted"> 2 days ago</abp-card-footer>
</abp-card>
</abp-component-demo-section>
<abp-component-demo-section title="Card Styles" view-path="@CardsDemoViewComponent.ViewPath">
<abp-card background="Primary" class="mb-3" style="max-width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Success" text-color="Danger" border="Dark" class="mb-3" style="max-width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Warning" text-color="Secondary" class="mb-3" style="max-width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Light" text-color="Dark" border="Success" class="mb-3" style="max-width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Dark" text-color="White" border="Danger" class="mb-3" style="max-width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Danger" class="mb-3" style="max-width: 18rem;">
<abp-card-header text-color="Primary">Featured</abp-card-header>
<abp-card-body>
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
<abp-card background="Info" border="Danger" class="mb-3" style="max-width: 18rem;">
<abp-card-header>Featured</abp-card-header>
<abp-card-body text-color="Danger">
<abp-card-title> Special title treatment</abp-card-title>
<abp-card-text>With supporting text below as a natural lead-in to additional content.</abp-card-text>
</abp-card-body>
</abp-card>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CollapseDemo/CollapseDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.CollapseDemo
{
[Widget]
public class CollapseDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/CollapseDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

43
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/CollapseDemo/Default.cshtml

@ -0,0 +1,43 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.CollapseDemo
<abp-component-demo-section title="Example" view-path="@CollapseDemoViewComponent.ViewPath">
<abp-button button-type="Primary" abp-collapse-id="collapseExample" text="Button with data-target" />
<a abp-button="Primary" abp-collapse-id="collapseExample"> Link with href </a>
<abp-collapse-body id="collapseExample">
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-collapse-body>
</abp-component-demo-section>
<abp-component-demo-section title="Multiple targets" view-path="@CollapseDemoViewComponent.ViewPath">
<a abp-button="Primary" abp-collapse-id="FirstCollapseExample"> Toggle first element </a>
<abp-button button-type="Primary" abp-collapse-id="SecondCollapseExample" text="Toggle second element" />
<abp-button button-type="Primary" abp-collapse-id="FirstCollapseExample SecondCollapseExample" text="Toggle both elements" />
<abp-row class="mt-3">
<abp-column size-sm="_6">
<abp-collapse-body id="FirstCollapseExample" multi="true">
Curabitur porta porttitor libero eu luctus. Praesent ultrices mattis commodo. Integer sodales massa risus, in molestie enim sagittis blandit
</abp-collapse-body>
</abp-column>
<abp-column size-sm="_6">
<abp-collapse-body id="SecondCollapseExample" multi="true">
Anim pariatur wolf moon tempor,,, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et.
</abp-collapse-body>
</abp-column>
</abp-row>
</abp-component-demo-section>
<abp-component-demo-section title="Accordion example" view-path="@CollapseDemoViewComponent.ViewPath">
<abp-accordion>
<abp-accordion-item title="Collapsible Group Item #1">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry rtat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
<abp-accordion-item title="Collapsible Group Item #2">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
<abp-accordion-item title="Collapsible Group Item #3">
Anim pariatur wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.
</abp-accordion-item>
</abp-accordion>
</abp-component-demo-section>

171
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/DropdownsDemo/Default.cshtml

@ -0,0 +1,171 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.DropdownsDemo
<abp-component-demo-section title="Single button" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button text="Dropdown button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Single button 2" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" link="true" text="Dropdown button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Single button 3" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Danger" text="Dropdown button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Split button" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Danger" dropdown-style="Split" text="Dropdown button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Split button 2" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button size="Large" button-type="Secondary" text="Large button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown><abp-dropdown>
<abp-dropdown-button size="Large" button-type="Secondary" dropdown-style="Split" text="Large split button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown><abp-dropdown>
<abp-dropdown-button size="Small" button-type="Secondary" text="Small button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown><abp-dropdown>
<abp-dropdown-button size="Small" button-type="Secondary" dropdown-style="Split" text="Small split button" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Directions" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown direction="Up">
<abp-dropdown-button button-type="Secondary" text="Dropup" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown><abp-dropdown direction="Right">
<abp-dropdown-button button-type="Secondary" text="dropright" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown><abp-dropdown direction="Right">
<abp-dropdown-button button-type="Secondary" dropdown-style="Split" text="Split right" />
<abp-dropdown-menu>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item href="#">Another action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Menu Items" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Dropdown" />
<abp-dropdown-menu>
<abp-dropdown-header>Dropdown Header</abp-dropdown-header>
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item>
<abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item-text>Dropdown Item Text</abp-dropdown-item-text>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Menu alignment" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Right-aligned" />
<abp-dropdown-menu align="Right">
<abp-dropdown-item href="#">Action</abp-dropdown-item>
<abp-dropdown-item active="true" href="#">Active action</abp-dropdown-item>
<abp-dropdown-item disabled="true" href="#">Disabled action</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="Custom Content" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Dropdown With Only Text" />
<abp-dropdown-menu class="p-4" style="max-width: 200px;">
<p>
Some example text that's free-flowing within the dropdown menu.
</p>
<p class="mb-0">
And this is more example text.
</p>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>
<abp-component-demo-section title="With Form" view-path="@DropdownsDemoViewComponent.ViewPath">
<abp-dropdown>
<abp-dropdown-button button-type="Secondary" text="Dropdown With Form" />
<abp-dropdown-menu>
<form class="px-4 py-3">
<abp-input asp-for="EmailAddress"></abp-input>
<abp-input asp-for="Password"></abp-input>
<abp-input asp-for="RememberMe"></abp-input>
<abp-button button-type="Primary" text="Sign In" type="submit" />
</form>
<abp-dropdown-divider></abp-dropdown-divider>
<abp-dropdown-item href="#">New around here? Sign up</abp-dropdown-item>
<abp-dropdown-item href="#">Forgot password?</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/DropdownsDemo/DropdownsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.DropdownsDemo
{
[Widget]
public class DropdownsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/DropdownsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

223
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/GridsDemo/Default.cshtml

@ -0,0 +1,223 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.GridsDemo
<abp-component-demo-section title="Equal-width" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column>1 of 2</abp-column>
<abp-column>2 of 2</abp-column>
</abp-row>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column>2 of 3</abp-column>
<abp-column>3 of 3</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Column Breaker" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column-breaker />
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Setting one column width" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column size="_6">2 of 3 (wider)</abp-column>
<abp-column>3 of 3</abp-column>
</abp-row>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column size="_5">2 of 3 (wider)</abp-column>
<abp-column>3 of 3</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Variable width content" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row h-align="Center">
<abp-column size-lg="_2">1 of 3</abp-column>
<abp-column size-md="Auto">Variable width content</abp-column>
<abp-column size-lg="_2">3 of 3</abp-column>
</abp-row>
<abp-row>
<abp-column>1 of 3</abp-column>
<abp-column size-md="Auto">Variable width content</abp-column>
<abp-column size-lg="_2">3 of 3</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Responsive classes" view-path="@GridsDemoViewComponent.ViewPath">
<abp-row>
<abp-column>col</abp-column>
<abp-column>col</abp-column>
<abp-column>col</abp-column>
<abp-column>col</abp-column>
</abp-row>
<abp-row>
<abp-column size="_8">col-8</abp-column>
<abp-column size="_4">col-4</abp-column>
</abp-row>
</abp-component-demo-section>
<abp-component-demo-section title="All breakpoints" view-path="@GridsDemoViewComponent.ViewPath">
<abp-row>
<abp-column size-sm="_8">col-sm-8</abp-column>
<abp-column size-sm="_4">col-sm-4</abp-column>
</abp-row>
<abp-row>
<abp-column size-sm="_">col-sm</abp-column>
<abp-column size-sm="_">col-sm</abp-column>
<abp-column size-sm="_">col-sm</abp-column>
<abp-column size-sm="_">col-sm</abp-column>
</abp-row>
</abp-component-demo-section>
<abp-component-demo-section title="Mix and match" view-path="@GridsDemoViewComponent.ViewPath">
<!-- Stack the columns on mobile by making one full-width and the other half-width -->
<abp-row>
<abp-column size="_12" size-md="_8">.col-12 .col-md-8</abp-column>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
</abp-row>
<!-- Columns start at 50% wide on mobile and bump up to 33.3% wide on desktop -->
<abp-row>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
<abp-column size="_6" size-md="_4">.col-6 .col-md-4</abp-column>
</abp-row>
<!-- Columns are always 50% wide, on mobile and desktop -->
<abp-row>
<abp-column size="_6">.col-6</abp-column>
<abp-column size="_6">.col-6</abp-column>
</abp-row>
</abp-component-demo-section>
<abp-component-demo-section title="Vertical Alignment" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row v-align="Start">
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
<abp-row v-align="Center">
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
<abp-row v-align="End">
<abp-column>column</abp-column>
<abp-column>column</abp-column>
<abp-column>column</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Vertical Alignment 2" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row v-align="Start">
<abp-column v-align="Start">column</abp-column>
<abp-column v-align="Center">column</abp-column>
<abp-column v-align="End">column</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Horizontal alignment" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row h-align="Start">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="Center">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="End">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="Around">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
<abp-row h-align="Between">
<abp-column size="_4">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="No gutters" view-path="@GridsDemoViewComponent.ViewPath">
<abp-row gutters="false">
<abp-column size="_8">One of two columns</abp-column>
<abp-column size="_4">One of two columns</abp-column>
</abp-row>
</abp-component-demo-section>
<abp-component-demo-section title="Column wrapping" view-path="@GridsDemoViewComponent.ViewPath">
<abp-row>
<abp-column size="_9">.col-9</abp-column>
<abp-column size="_4">.col-4<br>Since 9 + 4 = 13 &gt; 12, this 4-column-wide div gets wrapped onto a new line as one contiguous unit.</abp-column>
<abp-column size="_6">.col-6<br>Subsequent columns continue along the new line.s</abp-column>
</abp-row>
</abp-component-demo-section>
<abp-component-demo-section title="Reordering" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column order="_12">First, but Last</abp-column>
<abp-column>Second, but unordered</abp-column>
<abp-column order="_6">Third, but Second</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Reordering 2" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column order="Last">First, but Last</abp-column>
<abp-column>Second, but unordered</abp-column>
<abp-column order="First">Third, but First</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Offsetting columns" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column size-md="_4">.col-md-4</abp-column>
<abp-column size-md="_4" offset-md="_4">.col-md-4 .offset-md-4</abp-column>
</abp-row>
<abp-row>
<abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column>
<abp-column size-md="_3" offset-md="_3">.col-md-3 .offset-md-3</abp-column>
</abp-row>
<abp-row>
<abp-column size-md="_6" offset-md="_3">.col-md-6 .offset-md-3</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>
<abp-component-demo-section title="Offsetting columns 2" view-path="@GridsDemoViewComponent.ViewPath">
<abp-container>
<abp-row>
<abp-column size-sm="_5" size-md="_6">.col-sm-5 .col-md-6</abp-column>
<abp-column size-sm="_5" offset-sm="_2" size-md="_6" offset-md="_">.col-sm-5 .offset-sm-2 .col-md-6 .offset-md-0</abp-column>
</abp-row>
<abp-row>
<abp-column size-sm="_6" size-md="_5" size-lg="_6">col-sm-6 .col-md-5 .col-lg-6</abp-column>
<abp-column size-sm="_6" size-md="_5" offset-md="_2" size-lg="_6" offset-lg="_">.col-sm-6 .col-md-5 .offset-md-2 .col-lg-6 .offset-lg-0</abp-column>
</abp-row>
</abp-container>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/GridsDemo/GridsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.GridsDemo
{
[Widget]
public class GridsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/GridsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

82
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ListGroupsDemo/Default.cshtml

@ -0,0 +1,82 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ListGroupsDemo
<abp-component-demo-section title="Basic example" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Morbi leo risus</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="Active & disabled items" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item active="true">Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Morbi leo risus</abp-list-group-item>
<abp-list-group-item disabled="true">Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="Links and buttons" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item href="#" active="true">Cras justo odio</abp-list-group-item>
<abp-list-group-item href="#">Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item href="#">Morbi leo risus</abp-list-group-item>
<abp-list-group-item href="#" disabled="true">Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="Links and buttons 2" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item href="#" active="true">Cras justo odio</abp-list-group-item>
<abp-list-group-item href="#">Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item href="#">Morbi leo risus</abp-list-group-item>
<abp-list-group-item href="#" disabled="true">Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="Flush" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group flush="true">
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in</abp-list-group-item>
<abp-list-group-item>Morbi leo risus</abp-list-group-item>
<abp-list-group-item>Vestibulum at eros</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="Contextual classes" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item>Cras justo odio</abp-list-group-item>
<abp-list-group-item type="Primary">A simple Primary list group item</abp-list-group-item>
<abp-list-group-item type="Secondary">A simple Secondary list group item</abp-list-group-item>
<abp-list-group-item type="Success">A simple Success list group item</abp-list-group-item>
<abp-list-group-item type="Danger">A simple Danger list group item</abp-list-group-item>
<abp-list-group-item type="Warning">A simple Warning list group item</abp-list-group-item>
<abp-list-group-item type="Info">A simple Info list group item</abp-list-group-item>
<abp-list-group-item type="Light">A simple Light list group item</abp-list-group-item>
<abp-list-group-item type="Dark">A simple Dark list group item</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="Contextual classes 2" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item href="#">Cras justo odio</abp-list-group-item>
<abp-list-group-item href="#" type="Primary">A simple Primary list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Secondary">A simple Secondary list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Success">A simple Success list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Danger">A simple Danger list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Warning">A simple Warning list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Info">A simple Info list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Light">A simple Light list group item</abp-list-group-item>
<abp-list-group-item href="#" type="Dark">A simple Dark list group item</abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>
<abp-component-demo-section title="With badges" view-path="@ListGroupsDemoViewComponent.ViewPath">
<abp-list-group>
<abp-list-group-item>Cras justo odio <span abp-badge-pill="Primary">14</span></abp-list-group-item>
<abp-list-group-item>Dapibus ac facilisis in <span abp-badge-pill="Primary">2</span></abp-list-group-item>
<abp-list-group-item>Morbi leo risus <span abp-badge-pill="Primary">1</span></abp-list-group-item>
</abp-list-group>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ListGroupsDemo/ListGroupsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ListGroupsDemo
{
[Widget]
public class ListGroupsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/ListGroupsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

15
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ModalsDemo/Default.cshtml

@ -0,0 +1,15 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ModalsDemo
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
<abp-component-demo-section title="Example" view-path="@ModalsDemoViewComponent.ViewPath">
<abp-button button-type="Primary" data-toggle="modal" data-target="#myModal">Launch modal</abp-button>
<abp-modal centered="true" size="Large" id="myModal">
<abp-modal-header title="Modal title"></abp-modal-header>
<abp-modal-body>
Woohoo, you're reading this text in a modal!
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Save|AbpModalButtons.Close)"></abp-modal-footer>
</abp-modal>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ModalsDemo/ModalsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ModalsDemo
{
[Widget]
public class ModalsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/ModalsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

53
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/NavsDemo/Default.cshtml

@ -0,0 +1,53 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.NavsDemo
<abp-component-demo-section title="Base nav" view-path="@NavsDemoViewComponent.ViewPath">
<abp-nav nav-style="Pill" align="Center">
<abp-nav-item>
<a abp-nav-link active="true" href="#">Active</a>
</abp-nav-item>
<abp-nav-item>
<a abp-nav-link href="#">Longer nav link</a>
</abp-nav-item>
<abp-nav-item>
<a abp-nav-link href="#">link</a>
</abp-nav-item>
<abp-nav-item>
<a abp-nav-link disabled="true" href="#">disabled</a>
</abp-nav-item>
</abp-nav>
</abp-component-demo-section>
<abp-component-demo-section title="Base nav 2" view-path="@NavsDemoViewComponent.ViewPath">
<abp-nav-bar size="Lg" navbar-style="Dark_Warning">
<a abp-navbar-brand href="#">Navbar</a>
<abp-navbar-toggle>
<abp-navbar-nav>
<abp-nav-item active="true">
<a abp-nav-link href="#">Home <span class="sr-only">(current)</span></a>
</abp-nav-item>
<abp-nav-item>
<a abp-nav-link href="#">Link</a>
</abp-nav-item>
<abp-nav-item dropdown="true">
<abp-dropdown>
<abp-dropdown-button nav-link="true" text="Dropdown" />
<abp-dropdown-menu>
<abp-dropdown-header>Dropdown header</abp-dropdown-header>
<abp-dropdown-item href="#" active="true">Action</abp-dropdown-item>
<abp-dropdown-item href="#" disabled="true">Another disabled action</abp-dropdown-item>
<abp-dropdown-item href="#">Something else here</abp-dropdown-item>
<abp-dropdown-divider />
<abp-dropdown-item href="#">Separated link</abp-dropdown-item>
</abp-dropdown-menu>
</abp-dropdown>
</abp-nav-item>
<abp-nav-item>
<a abp-nav-link disabled="true" href="#">Disabled</a>
</abp-nav-item>
</abp-navbar-nav>
<span abp-navbar-text>
Sample Text
</span>
</abp-navbar-toggle>
</abp-nav-bar>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/NavsDemo/NavsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.NavsDemo
{
[Widget]
public class NavsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/NavsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/PopoversDemo/Default.cshtml

@ -0,0 +1,16 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.PopoversDemo
<abp-component-demo-section title="Example" view-path="@PopoversDemoViewComponent.ViewPath">
<abp-button abp-popover="Hi, i'm popover content!">
Popover Default
</abp-button>
<abp-button abp-popover-top="Hi, i'm popover content!" title="Popover Title">
Popover With Title
</abp-button>
<abp-button abp-popover-right="Hi, i'm popover content!" title="Popover Title" dismissible="true">
Dismissible Popover
</abp-button>
<abp-button abp-popover-left="Hi, i'm popover content!" title="Popover Title" disabled="true">
Disabled Popover
</abp-button>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/PopoversDemo/PopoversDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.PopoversDemo
{
[Widget]
public class PopoversDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/PopoversDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

17
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ProgressBarsDemo/Default.cshtml

@ -0,0 +1,17 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ProgressBarsDemo
<abp-component-demo-section title="Example" view-path="@ProgressBarsDemoViewComponent.ViewPath">
<abp-progress-bar value="70" />
<abp-progress-bar type="Warning" value="25"> %25 </abp-progress-bar>
<abp-progress-bar type="Success" value="40" strip="true" />
<abp-progress-bar type="Dark" value="10" min-value="5" max-value="15" strip="true"> %50 </abp-progress-bar>
<abp-progress-group>
<abp-progress-part type="Success" value="25" />
<abp-progress-part type="Danger" value="10" strip="true"> %10 </abp-progress-part>
<abp-progress-part type="Primary" value="50" animation="true" strip="true" />
</abp-progress-group>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/ProgressBarsDemo/ProgressBarsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.ProgressBarsDemo
{
[Widget]
public class ProgressBarsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/ProgressBarsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

134
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TablesDemo/Default.cshtml

@ -0,0 +1,134 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.TablesDemo
<abp-component-demo-section title="Example 1" view-path="@TablesDemoViewComponent.ViewPath">
<abp-table hoverable-rows="true" responsive-sm="true">
<thead>
<tr>
<th scope="Column">#</th>
<th scope="Column">First</th>
<th scope="Column">Last</th>
<th scope="Column">Handle</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="Row">1</th>
<td>Mark</td>
<td>Otto</td>
<td table-style="Danger">mdo</td>
</tr>
<tr table-style="Warning">
<th scope="Row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>fat</td>
</tr>
<tr>
<th scope="Row">3</th>
<td table-style="Success">Larry</td>
<td>the Bird</td>
<td>twitter</td>
</tr>
</tbody>
</abp-table>
</abp-component-demo-section>
<abp-component-demo-section title="Example 2" view-path="@TablesDemoViewComponent.ViewPath">
<abp-table small="true" striped-rows="true" border-style="Bordered">
<thead Theme="Dark">
<tr>
<th scope="Column">#</th>
<th scope="Column">First</th>
<th scope="Column">Last</th>
<th scope="Column">Handle</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="Row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>mdo</td>
</tr>
<tr>
<th scope="Row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>fat</td>
</tr>
<tr>
<th scope="Row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>twitter</td>
</tr>
</tbody>
</abp-table>
</abp-component-demo-section>
<abp-component-demo-section title="Example 3" view-path="@TablesDemoViewComponent.ViewPath">
<abp-table striped-rows="true" dark-theme="true">
<caption>List of users</caption>
<thead>
<tr>
<th scope="Column">#</th>
<th scope="Column">First</th>
<th scope="Column">Last</th>
<th scope="Column">Handle</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="Row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>mdo</td>
</tr>
<tr>
<th scope="Row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>fat</td>
</tr>
<tr>
<th scope="Row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>twitter</td>
</tr>
</tbody>
</abp-table>
</abp-component-demo-section>
<abp-component-demo-section title="Example 4" view-path="@TablesDemoViewComponent.ViewPath">
<abp-table border-style="Borderless">
<thead>
<tr>
<th scope="Column">#</th>
<th scope="Column">First</th>
<th scope="Column">Last</th>
<th scope="Column">Handle</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="Row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>mdo</td>
</tr>
<tr>
<th scope="Row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>fat</td>
</tr>
<tr>
<th scope="Row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>twitter</td>
</tr>
</tbody>
</abp-table>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TablesDemo/TablesDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.TablesDemo
{
[Widget]
public class TablesDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/TablesDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

63
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TabsDemo/Default.cshtml

@ -0,0 +1,63 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.TabsDemo
<abp-component-demo-section title="Example" view-path="@TabsDemoViewComponent.ViewPath">
<abp-tabs>
<abp-tab title="Home">
Content_Home
</abp-tab>
<abp-tab-link title="Link" href="#" />
<abp-tab title="profile">
Content_Profile
</abp-tab>
<abp-tab-dropdown title="Contact" name="ContactDropdown">
<abp-tab title="Contact 1" parent-dropdown-name="ContactDropdown">
Content_1_Content
</abp-tab>
<abp-tab title="Contact 2" parent-dropdown-name="ContactDropdown">
Content_2_Content
</abp-tab>
</abp-tab-dropdown>
</abp-tabs>
</abp-component-demo-section>
<abp-component-demo-section title="Tab attributes" view-path="@TabsDemoViewComponent.ViewPath">
<abp-tabs>
<abp-tab name="nav-home" title="Home">
Content_Home
</abp-tab>
<abp-tab name="nav-profile" active="true" title="profile">
Content_Profile
</abp-tab>
<abp-tab name="nav-contact" title="Contact">
Content_Contact
</abp-tab>
</abp-tabs>
</abp-component-demo-section>
<abp-component-demo-section title="Pill Example" view-path="@TabsDemoViewComponent.ViewPath">
<abp-tabs tab-style="Pill">
<abp-tab title="Home">
Content_Home
</abp-tab>
<abp-tab title="profile">
Content_Profile
</abp-tab>
<abp-tab title="Contact">
Content_Contact
</abp-tab>
</abp-tabs>
</abp-component-demo-section>
<abp-component-demo-section title="Vertical Example" view-path="@TabsDemoViewComponent.ViewPath">
<abp-tabs tab-style="PillVertical" vertical-header-size="_2">
<abp-tab active="true" title="Home">
Content_Home
</abp-tab>
<abp-tab title="profile">
Content_Profile
</abp-tab>
<abp-tab title="Contact">
Content_Contact
</abp-tab>
</abp-tabs>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TabsDemo/TabsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.TabsDemo
{
[Widget]
public class TabsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/TabsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

23
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TooltipsDemo/Default.cshtml

@ -0,0 +1,23 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.TooltipsDemo
<abp-component-demo-section title="Example" view-path="@TooltipsDemoViewComponent.ViewPath">
<abp-button abp-tooltip="Tooltip">
Tooltip Default
</abp-button>
<abp-button abp-tooltip-top="Tooltip">
Tooltip on top
</abp-button>
<abp-button abp-tooltip-right="Tooltip">
Tooltip on right
</abp-button>
<abp-button abp-tooltip-bottom="Tooltip">
Tooltip on bottom
</abp-button>
<abp-button disabled="true" abp-tooltip="Tooltip">
Disabled button Tooltip
</abp-button>
</abp-component-demo-section>

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Views/Components/Themes/Shared/Demos/TooltipsDemo/TooltipsDemoViewComponent.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.Views.Components.Themes.Shared.Demos.TooltipsDemo
{
[Widget]
public class TooltipsDemoViewComponent : AbpViewComponent
{
public const string ViewPath = "/Views/Components/Themes/Shared/Demos/TooltipsDemo/Default.cshtml";
public IViewComponentResult Invoke()
{
return View(ViewPath);
}
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs

@ -84,12 +84,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing
return false;
}
//TODO: This is partially duplication of AuditHelper.ShouldSaveAudit method. Check why it does not work for controllers
if (!AuditingInterceptorRegistrar.ShouldAuditTypeByDefault(context.Controller.GetType()))
{
return false;
}
auditLog = auditLogScope.Log;
auditLogAction = _auditingHelper.CreateAuditLogAction(
auditLog,

2
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpAuditingMiddleware.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

0
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/FodyWeavers.xml → framework/src/Volo.Abp.BackgroundJobs.Quartz/FodyWeavers.xml

0
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/FodyWeavers.xsd → framework/src/Volo.Abp.BackgroundJobs.Quartz/FodyWeavers.xsd

22
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo.Abp.BackgroundJobs.Quartz.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.BackgroundJobs.Quartz</AssemblyName>
<PackageId>Volo.Abp.BackgroundJobs.Quartz</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.BackgroundJobs.Abstractions\Volo.Abp.BackgroundJobs.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Quartz\Volo.Abp.Quartz.csproj" />
</ItemGroup>
</Project>

18
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobsQuartzModule.cs

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Quartz;
namespace Volo.Abp.BackgroundJobs.Quartz
{
[DependsOn(
typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpQuartzModule)
)]
public class AbpBackgroundJobsQuartzModule :AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient(typeof(QuartzJobExecutionAdapter<>));
}
}
}

27
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs

@ -0,0 +1,27 @@
using System;
using System.Threading.Tasks;
using Quartz;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.BackgroundJobs.Quartz
{
[Dependency(ReplaceServices = true)]
public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDependency
{
private readonly IScheduler _scheduler;
public QuartzBackgroundJobManager(IScheduler scheduler)
{
_scheduler = scheduler;
}
public async Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
TimeSpan? delay = null)
{
var jobDetail = JobBuilder.Create<QuartzJobExecutionAdapter<TArgs>>().SetJobData(new JobDataMap { { nameof(TArgs), args } }).Build();
var trigger = !delay.HasValue ? TriggerBuilder.Create().StartNow().Build() : TriggerBuilder.Create().StartAt(new DateTimeOffset(DateTime.Now.Add(delay.Value))).Build();
await _scheduler.ScheduleJob(jobDetail, trigger);
return jobDetail.Key.ToString();
}
}
}

36
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs

@ -0,0 +1,36 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Quartz;
namespace Volo.Abp.BackgroundJobs.Quartz
{
public class QuartzJobExecutionAdapter<TArgs> : IJob
{
protected AbpBackgroundJobOptions Options { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; }
public QuartzJobExecutionAdapter(
IOptions<AbpBackgroundJobOptions> options,
IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory)
{
JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
}
public async Task Execute(IJobExecutionContext context)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var args = (TArgs)context.JobDetail.JobDataMap.Get(nameof(TArgs));
var jobType = Options.GetJob(typeof(TArgs)).JobType;
var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args);
await JobExecuter.ExecuteAsync(jobContext);
}
}
}
}

0
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.RabbitMq/FodyWeavers.xml → framework/src/Volo.Abp.BackgroundWorkers.Quartz/FodyWeavers.xml

0
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.RabbitMq/FodyWeavers.xsd → framework/src/Volo.Abp.BackgroundWorkers.Quartz/FodyWeavers.xsd

24
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo.Abp.BackgroundWorkers.Quartz.csproj

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.BackgroundWorkers.Quartz</AssemblyName>
<PackageId>Volo.Abp.BackgroundWorkers.Quartz</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.BackgroundWorkers\Volo.Abp.BackgroundWorkers.csproj" />
<ProjectReference Include="..\Volo.Abp.Quartz\Volo.Abp.Quartz.csproj" />
</ItemGroup>
</Project>

30
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs

@ -0,0 +1,30 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
using Volo.Abp.Quartz;
namespace Volo.Abp.BackgroundWorkers.Quartz
{
[DependsOn(
typeof(AbpBackgroundWorkersModule),
typeof(AbpQuartzModule)
)]
public class AbpBackgroundWorkersQuartzModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddConventionalRegistrar(new AbpQuartzConventionalRegistrar());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var backgroundWorkerManager = context.ServiceProvider.GetService<IBackgroundWorkerManager>();
var works = context.ServiceProvider.GetServices<IQuartzBackgroundWorker>();
foreach (var work in works)
{
backgroundWorkerManager.Add(work);
}
}
}
}

27
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpQuartzConventionalRegistrar.cs

@ -0,0 +1,27 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.BackgroundWorkers.Quartz
{
public class AbpQuartzConventionalRegistrar : DefaultConventionalRegistrar
{
public override void AddType(IServiceCollection services, Type type)
{
if (!typeof(IQuartzBackgroundWorker).IsAssignableFrom(type))
{
return;
}
var dependencyAttribute = GetDependencyAttributeOrNull(type);
var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);
if (lifeTime == null)
{
return;
}
services.Add(ServiceDescriptor.Describe(typeof(IQuartzBackgroundWorker), type, lifeTime.Value));
}
}
}

11
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorker.cs

@ -0,0 +1,11 @@
using Quartz;
namespace Volo.Abp.BackgroundWorkers.Quartz
{
public interface IQuartzBackgroundWorker : IBackgroundWorker, IJob
{
ITrigger Trigger { get; set; }
IJobDetail JobDetail { get; set; }
}
}

14
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerBase.cs

@ -0,0 +1,14 @@
using System.Threading.Tasks;
using Quartz;
namespace Volo.Abp.BackgroundWorkers.Quartz
{
public abstract class QuartzBackgroundWorkerBase : BackgroundWorkerBase, IQuartzBackgroundWorker
{
public ITrigger Trigger { get; set; }
public IJobDetail JobDetail { get; set; }
public abstract Task Execute(IJobExecutionContext context);
}
}

43
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs

@ -0,0 +1,43 @@
using System.Threading;
using System.Threading.Tasks;
using Quartz;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundWorkers.Quartz
{
[Dependency(ReplaceServices = true)]
public class QuartzBackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDependency
{
private readonly IScheduler _scheduler;
public QuartzBackgroundWorkerManager(IScheduler scheduler)
{
_scheduler = scheduler;
}
public async Task StartAsync(CancellationToken cancellationToken = default)
{
await _scheduler.ResumeAll(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken = default)
{
if (!_scheduler.IsShutdown)
{
await _scheduler.PauseAll(cancellationToken);
}
}
public void Add(IBackgroundWorker worker)
{
if (worker is IQuartzBackgroundWorker quartzWork)
{
Check.NotNull(quartzWork.Trigger, nameof(quartzWork.Trigger));
Check.NotNull(quartzWork.JobDetail, nameof(quartzWork.JobDetail));
AsyncHelper.RunSync(() => _scheduler.ScheduleJob(quartzWork.JobDetail, quartzWork.Trigger));
}
}
}
}

30
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs

@ -16,10 +16,12 @@ namespace Volo.Abp.Cli.Commands
public ILogger<AddModuleCommand> Logger { get; set; }
protected SolutionModuleAdder SolutionModuleAdder { get; }
public SolutionAbpVersionFinder SolutionAbpVersionFinder { get; }
public AddModuleCommand(SolutionModuleAdder solutionModuleAdder)
public AddModuleCommand(SolutionModuleAdder solutionModuleAdder, SolutionAbpVersionFinder solutionAbpVersionFinder)
{
SolutionModuleAdder = solutionModuleAdder;
SolutionAbpVersionFinder = solutionAbpVersionFinder;
Logger = NullLogger<AddModuleCommand>.Instance;
}
@ -34,14 +36,26 @@ namespace Volo.Abp.Cli.Commands
);
}
var withSourceCode = commandLineArgs.Options.ContainsKey("with-source-code");
var skipDbMigrations = Convert.ToBoolean(
commandLineArgs.Options.GetOrNull(Options.DbMigrations.Skip) ?? "false");
var solutionFile = GetSolutionFile(commandLineArgs);
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
if (version == null)
{
version = SolutionAbpVersionFinder.Find(solutionFile);
}
await SolutionModuleAdder.AddAsync(
GetSolutionFile(commandLineArgs),
solutionFile,
commandLineArgs.Target,
commandLineArgs.Options.GetOrNull(Options.StartupProject.Short, Options.StartupProject.Long),
skipDbMigrations
version,
skipDbMigrations,
withSourceCode
);
}
@ -57,8 +71,9 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp add-module <module-name> [options]");
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(" --with-source-code Downloads the source code of the module and adds it to your solution.");
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(" -sp|--startup-project <startup-project-path> Relative path to the project folder of the startup project. Default value is the current folder.");
sb.AppendLine("");
sb.AppendLine("Examples:");
@ -126,6 +141,11 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "s";
public const string Long = "solution";
}
public static class Version
{
public const string Short = "v";
public const string Long = "version";
}
public static class DbMigrations
{

81
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs

@ -7,6 +7,7 @@ using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.DependencyInjection;
@ -15,12 +16,14 @@ namespace Volo.Abp.Cli.Commands
{
public class GetSourceCommand : IConsoleCommand, ITransientDependency
{
private readonly SourceCodeDownloadService _sourceCodeDownloadService;
public ModuleProjectBuilder ModuleProjectBuilder { get; }
public ILogger<NewCommand> Logger { get; set; }
public GetSourceCommand(ModuleProjectBuilder moduleProjectBuilder)
public GetSourceCommand(ModuleProjectBuilder moduleProjectBuilder, SourceCodeDownloadService sourceCodeDownloadService)
{
_sourceCodeDownloadService = sourceCodeDownloadService;
ModuleProjectBuilder = moduleProjectBuilder;
Logger = NullLogger<NewCommand>.Instance;
}
@ -36,29 +39,13 @@ namespace Volo.Abp.Cli.Commands
);
}
Logger.LogInformation("Downloading source code of " + commandLineArgs.Target);
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
if (version != null)
{
Logger.LogInformation("Version: " + version);
}
var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long);
if (outputFolder != null)
{
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
outputFolder = Path.GetFullPath(outputFolder);
}
else
{
outputFolder = Directory.GetCurrentDirectory();
}
var outputFolder = GetOutPutFolder(commandLineArgs);
Logger.LogInformation("Output folder: " + outputFolder);
var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long);
@ -69,52 +56,28 @@ namespace Volo.Abp.Cli.Commands
commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command);
var result = await ModuleProjectBuilder.BuildAsync(
new ProjectBuildArgs(
SolutionName.Parse(commandLineArgs.Target),
commandLineArgs.Target,
version,
DatabaseProvider.NotSpecified,
UiFramework.NotSpecified,
gitHubLocalRepositoryPath,
commandLineArgs.Options
)
);
using (var templateFileStream = new MemoryStream(result.ZipContent))
await _sourceCodeDownloadService.DownloadAsync(
commandLineArgs.Target, outputFolder, version, gitHubLocalRepositoryPath, commandLineArgs.Options);
}
private static string GetOutPutFolder(CommandLineArgs commandLineArgs)
{
var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long);
if (outputFolder != null)
{
using (var zipInputStream = new ZipInputStream(templateFileStream))
if (!Directory.Exists(outputFolder))
{
var zipEntry = zipInputStream.GetNextEntry();
while (zipEntry != null)
{
var fullZipToPath = Path.Combine(outputFolder, zipEntry.Name);
var directoryName = Path.GetDirectoryName(fullZipToPath);
if (!string.IsNullOrEmpty(directoryName))
{
Directory.CreateDirectory(directoryName);
}
var fileName = Path.GetFileName(fullZipToPath);
if (fileName.Length == 0)
{
zipEntry = zipInputStream.GetNextEntry();
continue;
}
var buffer = new byte[4096]; // 4K is optimum
using (var streamWriter = File.Create(fullZipToPath))
{
StreamUtils.Copy(zipInputStream, streamWriter, buffer);
}
zipEntry = zipInputStream.GetNextEntry();
}
Directory.CreateDirectory(outputFolder);
}
outputFolder = Path.GetFullPath(outputFolder);
}
else
{
outputFolder = Directory.GetCurrentDirectory();
}
Logger.LogInformation($"'{commandLineArgs.Target}' has been successfully downloaded to '{outputFolder}'");
return outputFolder;
}
public string GetUsageInfo()

27
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -67,6 +67,12 @@ namespace Volo.Abp.Cli.Commands
Logger.LogInformation("UI Framework: " + uiFramework);
}
var mobileApp = GetMobilePreference(commandLineArgs);
if (mobileApp != MobileApp.None)
{
Logger.LogInformation("Mobile App: " + mobileApp);
}
var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long);
if (gitHubLocalRepositoryPath != null)
{
@ -94,6 +100,7 @@ namespace Volo.Abp.Cli.Commands
version,
databaseProvider,
uiFramework,
mobileApp,
gitHubLocalRepositoryPath,
commandLineArgs.Options
)
@ -208,6 +215,20 @@ namespace Volo.Abp.Cli.Commands
}
}
private MobileApp GetMobilePreference(CommandLineArgs commandLineArgs)
{
var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long);
switch (optionValue)
{
case "none":
return MobileApp.None;
case "react-native":
return MobileApp.ReactNative;
default:
return MobileApp.ReactNative;
}
}
public static class Options
{
public static class Template
@ -244,6 +265,12 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "u";
public const string Long = "ui";
}
public static class Mobile
{
public const string Short = "m";
public const string Long = "mobile";
}
}
}
}

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/AbpNuGetIndexUrlService.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Licensing;

83
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands.Services
{
public class SourceCodeDownloadService : ITransientDependency
{
public ModuleProjectBuilder ModuleProjectBuilder { get; }
public ILogger<SourceCodeDownloadService> Logger { get; set; }
public SourceCodeDownloadService(ModuleProjectBuilder moduleProjectBuilder)
{
ModuleProjectBuilder = moduleProjectBuilder;
Logger = NullLogger<SourceCodeDownloadService>.Instance;
}
public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubLocalRepositoryPath, AbpCommandLineOptions options)
{
Logger.LogInformation("Downloading source code of " + moduleName);
Logger.LogInformation("Version: " + version);
Logger.LogInformation("Output folder: " + outputFolder);
var result = await ModuleProjectBuilder.BuildAsync(
new ProjectBuildArgs(
SolutionName.Parse(moduleName),
moduleName,
version,
DatabaseProvider.NotSpecified,
UiFramework.NotSpecified,
null,
gitHubLocalRepositoryPath,
options
)
);
using (var templateFileStream = new MemoryStream(result.ZipContent))
{
using (var zipInputStream = new ZipInputStream(templateFileStream))
{
var zipEntry = zipInputStream.GetNextEntry();
while (zipEntry != null)
{
var fullZipToPath = Path.Combine(outputFolder, zipEntry.Name);
var directoryName = Path.GetDirectoryName(fullZipToPath);
if (!string.IsNullOrEmpty(directoryName))
{
Directory.CreateDirectory(directoryName);
}
var fileName = Path.GetFileName(fullZipToPath);
if (fileName.Length == 0)
{
zipEntry = zipInputStream.GetNextEntry();
continue;
}
var buffer = new byte[4096]; // 4K is optimum
using (var streamWriter = File.Create(fullZipToPath))
{
StreamUtils.Copy(zipInputStream, streamWriter, buffer);
}
zipEntry = zipInputStream.GetNextEntry();
}
}
}
Logger.LogInformation($"'{moduleName}' has been successfully downloaded to '{outputFolder}'");
}
}
}

25
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/MobileApp.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.Cli.ProjectBuilding.Building
{
public enum MobileApp
{
None,
ReactNative
}
public static class MobileAppExtensions{
public static string GetFolderName(this MobileApp mobileApp)
{
switch (mobileApp)
{
case MobileApp.ReactNative:
return "react-native";
default:
return null;
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save