Browse Source

Merge remote-tracking branch 'abpframework/master' into Translate

pull/1200/head
liangshiwei 7 years ago
parent
commit
9211785b79
  1. 1
      .gitignore
  2. 2
      abp_io/README.md
  3. 2
      abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj
  4. 1
      abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json
  5. 2
      abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj
  6. 4
      abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs
  7. 3
      build-all.ps1
  8. 2
      common.props
  9. 14
      docs/docs-langs.json
  10. 8
      docs/en/AspNetCore/Auto-API-Controllers.md
  11. 16
      docs/en/AspNetCore/Client-Side-Package-Management.md
  12. 2
      docs/en/Best-Practices/Entities.md
  13. 137
      docs/en/CLI.md
  14. 3
      docs/en/Data-Seeding.md
  15. 4
      docs/en/Exception-Handling.md
  16. 75
      docs/en/Getting-Started-AspNetCore-MVC-Template.md
  17. 2
      docs/en/Index.md
  18. 2
      docs/en/Microservice-Architecture.md
  19. 2
      docs/en/Module-Development-Basics.md
  20. 2
      docs/en/Modules/Index.md
  21. 2
      docs/en/Multi-Tenancy.md
  22. 4
      docs/en/Repositories.md
  23. 4
      docs/en/Samples/Microservice-Demo.md
  24. 11
      docs/en/Startup-Templates/Index.md
  25. 6
      docs/en/Startup-Templates/Mvc-Module.md
  26. 183
      docs/en/Startup-Templates/Mvc.md
  27. 149
      docs/en/Tutorials/AspNetCore-Mvc/Part-I.md
  28. 68
      docs/en/Tutorials/AspNetCore-Mvc/Part-II.md
  29. 87
      docs/en/Tutorials/AspNetCore-Mvc/Part-III.md
  30. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png
  31. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png
  32. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png
  33. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png
  34. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png
  35. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png
  36. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png
  37. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png
  38. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png
  39. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png
  40. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png
  41. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png
  42. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png
  43. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png
  44. 3
      docs/en/Value-Types.md
  45. 4
      docs/en/docs-nav.json
  46. BIN
      docs/en/images/bookstore-user-management-v2.png
  47. BIN
      docs/en/images/bookstore-user-management.png
  48. BIN
      docs/en/images/bookstore-visual-studio-solution-v2.png
  49. BIN
      docs/en/images/bookstore-visual-studio-solution-v3.png
  50. BIN
      docs/en/images/bookstore-visual-studio-solution.png
  51. BIN
      docs/en/images/db-migrator-app.png
  52. 4
      docs/zh-Hans/AspNetCore/Auto-API-Controllers.md
  53. 2
      docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md
  54. 4
      docs/zh-Hans/Best-Practices/Entities.md
  55. 137
      docs/zh-Hans/CLI.md
  56. 3
      docs/zh-Hans/Data-Seeding.md
  57. 2
      docs/zh-Hans/Exception-Handling.md
  58. 6
      docs/zh-Hans/Getting-Started-AspNetCore-Application.md
  59. 69
      docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md
  60. 92
      docs/zh-Hans/Index.md
  61. 37
      docs/zh-Hans/Samples/Microservice-Demo.md
  62. 8
      docs/zh-Hans/Startup-Templates/Index.md
  63. 6
      docs/zh-Hans/Startup-Templates/Mvc-Module.md
  64. 183
      docs/zh-Hans/Startup-Templates/Mvc.md
  65. 142
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md
  66. 66
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md
  67. 89
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md
  68. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png
  69. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png
  70. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png
  71. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png
  72. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png
  73. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png
  74. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png
  75. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png
  76. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png
  77. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png
  78. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png
  79. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png
  80. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png
  81. BIN
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png
  82. 3
      docs/zh-Hans/Value-Types.md
  83. 4
      docs/zh-Hans/docs-nav.json
  84. BIN
      docs/zh-Hans/images/bookstore-user-management-v2.png
  85. BIN
      docs/zh-Hans/images/bookstore-user-management.png
  86. BIN
      docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png
  87. BIN
      docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png
  88. BIN
      docs/zh-Hans/images/bookstore-visual-studio-solution.png
  89. BIN
      docs/zh-Hans/images/db-migrator-app.png
  90. 70
      framework/Volo.Abp.sln
  91. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs
  92. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  93. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs
  94. 26
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  95. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DynamicFormIgnore.cs
  96. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs
  97. 27
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json
  98. 35
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj
  99. 20
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs
  100. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs

1
.gitignore

@ -268,7 +268,6 @@ modules/blog/app/Volo.BlogTestApp/Logs/*.*
modules/blogging/app/Volo.BloggingTestApp/Logs/*.*
modules/blogging/app/Volo.BloggingTestApp/wwwroot/files/*.*
modules/docs/app/VoloDocs.Web/Logs/*.*
samples/BookStore/src/Acme.BookStore.Web/Logs/*.*
templates/module/app/MyCompanyName.MyProjectName.DemoApp/Logs/*.*
templates/mvc/src/MyCompanyName.MyProjectName.Web/Logs/*.*

2
abp_io/README.md

@ -1,2 +1,2 @@
## abp.op
## abp.io
Source code of the official web site: https://abp.io

2
abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj

@ -12,7 +12,7 @@
<ItemGroup>
<PackageReference Include="Ionic.Zip" Version="1.9.1.8" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="HtmlAgilityPack.NetCore" Version="1.5.0.1" />
</ItemGroup>

1
abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json

@ -3,6 +3,7 @@
"TemplateFiles/": {},
"Logs/": {},
"wwwroot/files/": {},
"wwwroot/downloads/": {},
"appsettings.json": {},
"web.config": {}
}

2
abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj

@ -15,7 +15,7 @@
<ItemGroup>
<PackageReference Include="Ionic.Zip" Version="1.9.1.8" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="HtmlAgilityPack.NetCore" Version="1.5.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />

4
abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs

@ -21,9 +21,9 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
var lines = file.GetLines();
for (var i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("ProjectReference") && lines[i].Contains("MyCompanyName.MyProjectName.EntityFrameworkCore"))
if (lines[i].Contains("ProjectReference") && lines[i].Contains("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations"))
{
lines[i] = lines[i].Replace("EntityFrameworkCore", "MongoDB");
lines[i] = lines[i].Replace("EntityFrameworkCore.DbMigrations", "MongoDB");
file.SetLines(lines);
return;
}

3
build-all.ps1

@ -20,8 +20,7 @@ $solutionPaths = (
"modules/background-jobs",
"modules/client-simulation",
"abp_io",
"templates/module",
"templates/service",
"templates/mvc-module",
"templates/mvc",
"samples/MicroserviceDemo"
)

2
common.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>0.17.0</Version>
<Version>0.18.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io</PackageProjectUrl>

14
docs/docs-langs.json

@ -0,0 +1,14 @@
{
"Languages":[
{
"DisplayName" : "English",
"Code" : "en",
"IsDefault": true
},
{
"DisplayName" : "简体中文",
"Code" : "zh-Hans",
"IsDefault": false
}
]
}

8
docs/en/AspNetCore/Auto-API-Controllers.md

@ -1,6 +1,6 @@
# Auto API Controllers
Once you create an [application service](Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc.
Once you create an [application service](../Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc.
ABP can **automagically** configures your application services as MVC API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible fully customize it.
@ -16,7 +16,9 @@ public class BookStoreWebModule : AbpModule
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly);
options
.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly);
});
}
}
@ -106,7 +108,7 @@ public class PersonAppService : ApplicationService
### TypePredicate Option
You can further filter classes to become an API controller by providing the `TypePedicate` option:
You can further filter classes to become an API controller by providing the `TypePredicate` option:
````csharp
services.Configure<AbpAspNetCoreMvcOptions>(options =>

16
docs/en/AspNetCore/Client-Side-Package-Management.md

@ -22,14 +22,14 @@ The benefit of a **standard package** is:
Depending on a standard package is easy. Just add it to your **package.json** file like you normally do. Example:
{
...
"dependencies": {
"@abp/bootstrap": "^1.0.0"
}
}
````
{
...
"dependencies": {
"@abp/bootstrap": "^1.0.0"
}
}
````
It's suggested to depend on a standard package instead of directly depending on a third-party package.

2
docs/en/Best-Practices/Entities.md

@ -63,7 +63,7 @@ public class Issue : FullAuditedAggregateRoot<Guid> //Using Guid as the key/iden
protected Issue()
{
/* This conctructor is for ORMs to be used while getting the entity from database.
/* This constructor is for ORMs to be used while getting the entity from database.
* - No need to initialize the Labels collection
since it will be overrided from the database.
- It's protected since proxying and deserialization tools

137
docs/en/CLI.md

@ -0,0 +1,137 @@
# ABP CLI
ABP CLI (Command Line Interface) is a command line tool to perform some common operations for ABP based solutions.
## Installation
ABP CLI is a [dotnet global tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools). Install it using a command line window:
````bash
dotnet tool install -g Volo.Abp.Cli
````
To update an existing installation:
````bash
dotnet tool update -g Volo.Abp.Cli
````
## Commands
### new
Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md).
Basic usage:
````bash
abp new <solution-name> [options]
````
Example:
````bash
abp new Acme.BookStore
````
* Acme.BookStore is the solution name here.
* Common convention is to name a solution is like *YourCompany.YourProject*. However, you can use different naming like *YourProject* (single level namespacing) or *YourCompany.YourProduct.YourModule* (three levels namespacing).
#### Options
* `--template` or `-t`: Specifies the template name. Default template name is `mvc`. Available templates:
* `mvc` (default): ASP.NET Core [MVC application template](Startup-Templates/Mvc.md). Additional options:
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
* `mvc-module`: ASP.NET Core [MVC module template](Startup-Templates/Mvc-Module.md). Additional options:
* `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI).
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
### add-package
Adds a new ABP package to a project by,
* Adding related nuget package as a dependency to the project.
* Adding `[DependsOn(...)]` attribute to the module class in the project (see the [module development document](Module-Development-Basics.md)).
> Notice that the added module may require additional configuration which is generally indicated in the documentation of the related package.
Basic usage:
````bash
abp add-package <package-name> [options]
````
Example:
````
abp add-package Volo.Abp.MongoDB
````
* This example adds the Volo.Abp.MongoDB package to the project.
#### Options
* `--project` or `-p`: Specifies the project (.csproj) file path. If not specified, CLI tries to find a .csproj file in the current directory.
### add-module
Adds a multi-package module to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution.
> A business module generally consists of several packages (because of layering, different database providr options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Basic usage:
````bash
abp add-module <module-name> [options]
````
Example:
```bash
abp add-module Volo.Blogging
```
* This example add the Volo.Blogging module to the solution.
#### Options
* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation.
### update
Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related packages in a solution or project to the latest versions.
Usage:
````bash
abp update [options]
````
* If you run in a directory with a .sln file, it updates all ABP related packages of the all projects of the solution to the latest versions.
* If you run in a directory with a .csproj file, it updates all ABP related packages of the project to the latest versions.
#### Options
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions.
### help
Writes basic usage information of the CLI.
Usage:
````bash
abp help [command-name]
````
Examples:
````bash
abp help # Shows a general help.
abp help new # Shows help about the "new" command.
````

3
docs/en/Data-Seeding.md

@ -0,0 +1,3 @@
# Data Seeding
TODO

4
docs/en/Exception-Handling.md

@ -29,7 +29,7 @@ Error Message is an instance of the `RemoteServiceErrorResponse` class. The simp
}
````
There are **optional fields** those can be filled based upon the exception that has occured.
There are **optional fields** those can be filled based upon the exception that has occurred.
##### Error Code
@ -70,7 +70,7 @@ Error **details** in an optional field of the JSON error message. Thrown `Except
"code": "App:010046",
"message": "Your request is not valid, please correct and try again!",
"validationErrors": [{
"message": "Username should be minimum lenght of 3.",
"message": "Username should be minimum length of 3.",
"members": ["userName"]
},
{

75
docs/en/Getting-Started-AspNetCore-MVC-Template.md

@ -2,39 +2,44 @@
This tutorials explains how to create a new ASP.NET Core MVC web application using the startup template, configure and run it.
### Creating a new project
### Creating a New Project
Go to [the template creation page](https://abp.io/Templates), enter a project name and create your project as shown below:
This tutorial uses **ABP CLI** to create a new project. See the [Get Started](https://abp.io/get-started) page for other options.
![bookstore-create--template](images/bookstore-create-template.png)
Install the ABP CLI using a command line window, if you've not installed before:
When you click the *create* button, a new Visual Studio solution is created and downloaded with the name you have provided.
````bash
dotnet tool install -g Volo.Abp.Cli
````
#### Pre Requirements
Use `abp new` command in an empty folder to create your project:
The downloaded project requires;
````
abp new Acme.BookStore
````
* [Visual Studio 2017 (v15.7.0+)](https://visualstudio.microsoft.com/tr/downloads/)
* [.NET Core 2.1.1+](https://www.microsoft.com/net/download/dotnet-core/)
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
### The Solution Structure
`new` command creates a **layered MVC application** with **Entity Framework Core** as the database provider. However, it has additional options. See the [CLI documentation](CLI.md) for all available options.
Extract the zip file downloaded and open in **Visual Studio 2017 (15.9.0+)**:
#### Pre Requirements
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
The created solution requires;
The solution has a layered structure (based on Domain Driven Design) where;
* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/)
* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/)
* ``.Domain`` is the domain layer.
* ``.Application`` is the application layer.
* ``.Web`` is the presentation layer.
* ``.EntityFrameworkCore`` is the EF Core integration package.
### The Solution Structure
Open the solution in **Visual Studio**:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png)
EF Core database migrations are separated to a project named `.EntityFrameworkCore.DbMigrations`.
The solution has a layered structure (based on [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
The solution also contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
> See [MVC application template document](Startup-Templates/Mvc.md) to understand the solution structure in details.
### Creating the Database
### Database Connection String
Check the **connection string** in the `appsettings.json` file under the `.Web` project:
@ -46,9 +51,29 @@ Check the **connection string** in the `appsettings.json` file under the `.Web`
}
````
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want.
The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want. Change the connection string if you need.
### Create Database & Apply Database Migrations
You have two options to create the database.
Right click to the `.Web` project and select **Set as StartUp Project**:
#### Using the DbMigrator Application
The solution contains a console application (named `Acme.BookStore.DbMigrator` in this sample) that can create database, apply migrations and seed initial data. It is useful on development as well as on production environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
Right click to the `.DbMigrator` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below:
![set-as-startup-project](images/db-migrator-app.png)
#### Using EF Core Update-Database Command
Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations. Right click to the `.Web` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
@ -58,17 +83,19 @@ Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations`
This will create a new database based on the configured connection string.
> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application.
### Running the Application
You can now run the application which will open the **home** page:
Ensure that the `.Web` project is the startup project. Run the application which will open the **home** page in your browser:
![bookstore-homepage](images/bookstore-homepage.png)
Click the **Login** button, enter `admin` as the username and `1q2w3E*` as the password to login to the application.
The startup template includes the **identity management** module. Once you login, the Identity management menu will be available where you can manage **roles**, **users** and their **permissions**.
The startup template includes the **identity management** and **tenant management** modules. Once you login, the Administration menu will be available where you can manage **tenants**, **roles**, **users** and their **permissions**. User management page is shown below:
![bookstore-user-management](images/bookstore-user-management.png)
![bookstore-user-management](images/bookstore-user-management-v2.png)
### What's Next?

2
docs/en/Index.md

@ -6,7 +6,7 @@ Explore the left navigation menu to deep dive in the documentation.
## Project Status
ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is still in progress and it is incomplete yet.
ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is still in progress and it is far from complete.
For short-term and production level applications, it's suggested to use [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework which has rich feature set, mature, actively maintained and up-to-date.

2
docs/en/Microservice-Architecture.md

@ -19,7 +19,7 @@ One of the major goals of the ABP framework is to provide a convenient infrastru
## Microservice for New Applications
One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challanges of the microservice architecture.
One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challenges of the microservice architecture.
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts.

2
docs/en/Module-Development-Basics.md

@ -112,7 +112,7 @@ Lastly, you can override ``OnApplicationShutdown`` method if you want to execute
## Module Dependencies
In a modular application, it's not unusual for one module to depend upon another module(s). An Abp module must declare ``[DependsOn]`` attribute if it does have a dependcy upon another module, as shown below:
In a modular application, it's not unusual for one module to depend upon another module(s). An Abp module must declare ``[DependsOn]`` attribute if it does have a dependency upon another module, as shown below:
````C#
[DependsOn(typeof(AbpAspNetCoreMvcModule))]

2
docs/en/Modules/Index.md

@ -21,6 +21,6 @@ There are some **free and open source** application modules developed and mainta
* **Permission Management**: Used to persist permissions.
* **Setting Management**: Used to persist settings.
* **Tenant Management**: Used to manage tenants for a [multi-tenant](../Multi-Tenancy.md) application.
* **Users**: Used the abstract users, so other modules can depend on this instead of the Identity module.
* **Users**: Used to abstract users, so other modules can depend on this instead of the Identity module.
Documenting the modules is in the progress. See [this repository](https://github.com/abpframework/abp/tree/master/modules) for source code of all modules.

2
docs/en/Multi-Tenancy.md

@ -56,7 +56,7 @@ namespace MyCompany.MyProject
}
````
IMultiTenant requires to define a **TenantId** property in the implementing entity (See entity documentation (TODO: link) for more about entities).
IMultiTenant requires to define a **TenantId** property in the implementing entity (See [entity documentation](Entities.md) for more about entities).
#### Obtain Current Tenant's Id

4
docs/en/Repositories.md

@ -48,8 +48,8 @@ In this example;
Generic Repositories provides some standard CRUD features out of the box:
* Providers `Insert` method to save a new entity.
* Providers `Update` and `Delete` methods to update or delete an entity by entity object or it's id.
* Provides `Insert` method to save a new entity.
* Provides `Update` and `Delete` methods to update or delete an entity by entity object or it's id.
* Provides `Delete` method to delete multiple entities by a filter.
* Implements `IQueryable<TEntity>`, so you can use LINQ and extension methods like `FirstOrDefault`, `Where`, `OrderBy`, `ToList` and so on...
* Have **sync** and **async** versions for all methods.

4
docs/en/Samples/Microservice-Demo.md

@ -168,7 +168,7 @@ These are the actual applications those have user interfaces to interact to the
- **AuthServer.Host**: Host the IdentityServer4 to provide an authentication service to other services and applications. It is a single-sign server and contains the login page.
- **BackendAdminApp.Host**: This is a backend admin application that host UI for Identity and Product management modules.
- **PubicWebSite.Host**: As public web site that contains a simple product list page and blog module UI.
- **PublicWebSite.Host**: As public web site that contains a simple product list page and blog module UI.
- **ConsoleClientDemo**: A simple console application to demonstrate the usage of services from a C# application.
### Gateways / BFFs (Backend for Frontend)
@ -1471,4 +1471,4 @@ ABP provides automatic audit logging which saves every request in detail (who is
All of the services and applications are configured to write audit logs. Audit logs are saved to the MsDemo_Identity SQL database. So, you can query all audit logs of all applications from a single point.
An Audit Log record has a `CorrelationId` property that can be used to track a request. When a service calls another service in a single web request, they both save audit logs with the same `CorrelationId`. See the `AbpAuditLogs` table in the database.
An Audit Log record has a `CorrelationId` property that can be used to track a request. When a service calls another service in a single web request, they both save audit logs with the same `CorrelationId`. See the `AbpAuditLogs` table in the database.

11
docs/en/Startup-Templates/Index.md

@ -0,0 +1,11 @@
# Startup Templates
While you can start with an empty project and add needed packages manually, startup templates makes easy and comfortable to start a new solution with the ABP framework.
Click to the name from the list below to see the documentation of the related startup template:
* [**mvc**](Mvc.md): ASP.NET Core MVC application template.
* [**mvc-module**](Mvc-Module.md): ASP.NET Core MVC module/service template.

6
docs/en/Startup-Templates/Mvc-Module.md

@ -0,0 +1,6 @@
# MVC Module Startup Template
TODO

183
docs/en/Startup-Templates/Mvc.md

@ -0,0 +1,183 @@
# MVC Application Startup Template
## Introduction
This template provides a layered (or tiered, based on the preference) application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices.
## How to Start With
You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
First, install the ABP CLI as described in [its document](../CLI.md). Then use the `abp new` command in an empty folder to create a new solution:
````bash
abp new Acme.BookStore -t mvc
````
* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. See the [CLI document](../CLI.md) for different naming styles.
* This example specified the template name (`-t` or `--template` option). However, `mvc` is the default template and used even if you don't specify it.
### Specify Database Provider
This template supports the following database providers:
- `ef`: Entity Framework Core (default)
- `mongodb`: MongoDB
Use the `-d` (or `--database-provider`) to specify the database provider:
````bash
abp new Acme.BookStore -t mvc -d mongodb
````
### Create a Tiered Solution
`--tiered` option is used to create a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
````bash
abp new Acme.BookStore --tiered
````
See the "Tiered Structure" section below for the tiered approach.
## Solution Structure
Based on the options you've specified, you will get a slightly different solution structure.
### Default Structure
If you don't specify any option, you will have a solution like shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png)
Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
--------------------
**TODO: Add a graphic to illustrate dependencies between projects.**
------------------
Each section below will explain the related project.
#### .Domain Project
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects of the solution.
A `Book` entity and a `IBookRepository` interface are good candidates for this project.
* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
#### .Domain.Shared Project
This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
A `BookType` enum and a `BookConts` class (which may have some constant fields for the `Book` entity, like `MaxNameLength`) are good candidates for this project.
This project has no dependency to other projects in the solution.
#### .Application.Contracts Project
This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects in the application service interfaces and DTOs.
#### .Application Project
This project contains the [application service](../Application-Services.md) implementations of the interfaces defined in the `.Application.Contracts` project.
* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
#### .EntityFrameworkCore Project.
This is the integration project for the EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
* Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
> This project is available only if you are using EF Core as the database provider.
#### .EntityFrameworkCore.DbMigrations Project
Contains EF Cor database migrations for the solution. It has a separated `DbContext` to dedicated to manage migrations.
ABP is a modular framework and with an ideal design, each module has its own `DbContext` class. This is where the migration `DbContext` comes into play and unifies all `DbContext` configurations into a single model to maintain a single database schema.
Notice that the migration `DbContext` is only used for database migrations and *not used on runtime*.
* Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application.
> This project is available only if you are using EF Core as the database provider.
#### .DbMigrator Project
This is a console application which simplifies to execute database migrations on development and production environments. When you this application;
* Creates the database if necessary.
* Applies database migrations.
* Seeds initial data.
> This project has its own `appsettings.json` file. So, if you want to change the database connection string, remember to change this file.
Especially, seeding initial data is important at this point. ABP has a modular data seed infrastructure. See [its documentation](../Data-Seeding.md) for more about the data seeding.
While creating database & applying migrations seems only necessary for relational databases, this projects comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds initial data which is necessary for the application.
* Depends on the `.EntityFrameworkCore.DbMigrations` project (for EF Core) since it needs to access to the migrations.
* Depends on the `.Application.Contracts` project to be able to access permission definitions, because initial data seeder grants permissions for the admin user.
#### .HttpApi Project
This project is used to define your API Controllers.
Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it.
* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces.
#### .HttpApi.Client Project
This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications.
`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies.
Most of time you don't need to manually create C# client proxies, thanks to ABP's [Cynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature.
* Depends on the `.Application.Contracts` project to be able to share the same application service interfaces and DTOs with the remote service.
#### .Web Project
This project contains the User Interface (UI) of the application. It contains razor pages, javascript files, css files, images and so on...
* Depends on the `.HttpApi` since UI layer needs to use APIs and application service interfaces of the solution.
> If you check the source code of the `.Web.csproj` file, you will see the references to the `.Application` and the `.EntityFrameworkCore.DbMigrations` projects.
>
> These references are actually not needed on development, because UI layer normally doesn't depend on the EF Core or the Application implementation. This startup templates are ready for the tiered deployment, where API layer is hosted in a separate server than the UI layer.
>
> However, if you don't choose the `--tiered` option, these references will be in the .Web project to be able to host the Web, API and application layers in a single application endpoint.
>
> This gives you to ability to use domain entities in your presentation layer. However, this is considered as a bad practice according to the DDD.
#### Test Projects
The solution has multiple test projects, one for each layer:
* `.Domain.Tests` is used to test the domain layer.
* `.Application.Tests` is used to test the application layer.
* `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
* `.Web.Tests` is used to test the UI.
* `.TestBase` is a base (shared) project for all tests.
In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
### Tiered Structure
TODO
### Other Database Providers
TODO
#### MongoDB
TODO

149
docs/en/Tutorials/AspNetCore-Mvc/Part-I.md

@ -2,43 +2,45 @@
### About this Tutorial
In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider (as it comes pre-configured with the [startup template](https://abp.io/Templates)).
In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider as it is the default database provider.
This is the first part of the tutorial series. See all parts:
This is the first part of the ASP.NET Core MVC tutorial series. See all parts:
- **Part I: Create the project and a book list page (this tutorial)**
- [Part II: Create, Update and Delete books](Part-II.md)
- [Part III: Integration Tests](Part-III.md)
You can download the **source code** of the application [from here](https://github.com/volosoft/abp/tree/master/samples/BookStore).
You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore).
### Creating the Project
Go to the [startup template page](https://abp.io/Templates) and download a new project named `Acme.BookStore`, create the database and run the application by following the [template document](../../Getting-Started-AspNetCore-MVC-Template.md).
Create a new project named `Acme.BookStore`, create the database and run the application by following the [Getting Started document](../../Getting-Started-AspNetCore-MVC-Template.md).
### Solution Structure
This is the how the layered solution structure looks after it's created from the startup template:
This is how the layered solution structure looks after it's created:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png)
> You can see [MVC application template document](../../Startup-Templates/Mvc.md) to understand the solution structure in details. However, you will understand the basics with this tutorial.
### Create the Book Entity
Define [entities](../../Entities.md) in the **domain layer** (`Acme.BookStore.Domain` project) of the solution. The main entity of the application is the `Book`:
Domain layer in the startup template is separated into two projects:
- `Acme.BookStore.Domain` contains your [entities](../../Entities.md), [domain services](../../Domain-Services.md) and other core domain objects.
- `Acme.BookStore.Domain.Shared` contains constants, enums or other domain related objects those can be shared with clients.
Define [entities](../../Entities.md) in the **domain layer** (`Acme.BookStore.Domain` project) of the solution. The main entity of the application is the `Book`. Create a class, named `Book`, in the `Acme.BookStore.Domain` project as shown below:
````C#
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
[Table("Books")]
public class Book : AuditedAggregateRoot<Guid>
{
[Required]
[StringLength(128)]
public string Name { get; set; }
public BookType Type { get; set; }
@ -50,22 +52,22 @@ namespace Acme.BookStore
}
````
* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for more details and best practices.
* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for details and best practices.
* `Book` entity inherits `AuditedAggregateRoot` which adds some auditing properties (`CreationTime`, `CreatorId`, `LastModificationTime`... etc.) on top of the `AggregateRoot` class.
* `Guid` is the **primary key type** of the `Book` entity.
* Used **data annotation attributes** in this code for EF Core mappings. Alternatively you could use EF Core's [fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling) instead.
#### BookType Enum
The `BookType` enum used above is defined as below:
Define the `BookType` enum in the `Acme.BookStore.Domain.Shared` project:
````C#
namespace Acme.BookStore
{
public enum BookType : byte
public enum BookType
{
Undefined,
Advanture,
Adventure,
Biography,
Dystopia,
Fantastic,
@ -91,14 +93,15 @@ EF Core requires you to relate entities with your DbContext. The easiest way to
#### Configure Your Book Entity
Open BookStoreDbContextModelCreatingExtensions.cs file from the `Acme.BookStore.EntityFrameworkCore` project, add following code to the end of ConfigureBookStore method to configure Book entity:
Open `BookStoreDbContextModelCreatingExtensions.cs` file in the `Acme.BookStore.EntityFrameworkCore` project and add following code to the end of the `ConfigureBookStore` method to configure the Book entity:
````C#
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureExtraProperties();
});
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureAuditedAggregateRoot(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
````
#### Add New Migration & Update the Database
@ -115,26 +118,27 @@ PM> Update-Database
#### Add Sample Data
`Update-Database` command created the `Books` table in the database. Open your database and enter a few sample rows, so you can show them on the page:
`Update-Database` command created the `AppBooks` table in the database. Open your database and enter a few sample rows, so you can show them on the page:
![bookstore-books-table](images/bookstore-books-table.png)
### Create the Application Service
The next step is to create an [application service](../../Application-Services.md) to manage (create, list, update, delete...) the books.
The next step is to create an [application service](../../Application-Services.md) to manage (create, list, update, delete...) the books. Application layer in the startup template is separated into two projects:
* `Acme.BookStore.Application.Contracts` mainly contains your DTOs and application service interfaces.
* `Acme.BookStore.Application` contains the implementations of your application services.
#### BookDto
Create a DTO class named `BookDto` into the `Acme.BookStore.Application` project:
Create a DTO class named `BookDto` into the `Acme.BookStore.Application.Contracts` project:
````C#
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
[AutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -151,20 +155,34 @@ namespace Acme.BookStore
* **DTO** classes are used to **transfer data** between the *presentation layer* and the *application layer*. See the [Data Transfer Objects document](../../Data-Transfer-Objects.md) for more details.
* `BookDto` is used to transfer book data to the presentation layer in order to show the book information on the UI.
* `BookDto` is derived from the `AuditedEntityDto<Guid>` which has audit properties just like the `Book` class defined above.
* `[AutoMapFrom(typeof(Book))]` is used to create AutoMapper mapping from the `Book` class to the `BookDto` class. In this way, you get automatic convertion of `Book` objects to `BookDto` objects (instead of manually copy all properties).
It will be needed to convert `Book` entities to `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. Startup template comes with AutoMapper configured, so you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project:
````csharp
using AutoMapper;
namespace Acme.BookStore
{
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
}
}
}
````
#### CreateUpdateBookDto
Create a DTO class named `CreateUpdateBookDto` into the `Acme.BookStore.Application` project:
Create a DTO class named `CreateUpdateBookDto` into the `Acme.BookStore.Application.Contracts` project:
````c#
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
[AutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]
@ -184,11 +202,17 @@ namespace Acme.BookStore
````
* This DTO class is used to get book information from the user interface while creating or updating a book.
* It defines data annotation attributes (like `[Required]`) to define validations for the properties. DTOs are automatically validated by ABP.
* It defines data annotation attributes (like `[Required]`) to define validations for the properties. DTOs are [automatically validated](../../Validation.md) by the ABP framework.
Like done for the `BookDto` above, create a mapping from the `CreateUpdateBookDto` object to the `Book` entity:
````csharp
CreateMap<CreateUpdateBookDto, Book>();
````
#### IBookAppService
Define an interface named `IBookAppService` for the book application service:
Define an interface named `IBookAppService` in the `Acme.BookStore.Application.Contracts` project:
````C#
using System;
@ -210,13 +234,13 @@ namespace Acme.BookStore
}
````
* Defining interfaces for application services is <u>not required</u> by the framework. However, it's suggested as best practice.
* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods.
* There are some variations of the `IAsyncCrudAppService` where you can use a single DTO or separated DTOs for each method.
* Defining interfaces for application services is <u>not required</u> by the framework. However, it's suggested as a best practice.
* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods manually.
* There are some variations of the `IAsyncCrudAppService` where you can use separated DTOs for each method.
#### BookAppService
Implement the `IBookAppService` as named `BookAppService`:
Implement the `IBookAppService` as named `BookAppService` in the `Acme.BookStore.Application` project:
````C#
using System;
@ -241,23 +265,23 @@ namespace Acme.BookStore
````
* `BookAppService` is derived from `AsyncCrudAppService<...>` which implements all the CRUD methods defined above.
* `BookAppService` injects `IRepository<Book, Guid>` which is the default repository created for the `Book` entity. ABP automatically creates repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md).
* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as object mapping provider. You defined mappings using the `AutoMapFrom` and the `AutoMapTo` attributes above. See the [AutoMapper integration document](../../AutoMapper-Integration.md) for details.
* `BookAppService` injects `IRepository<Book, Guid>` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md).
* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. You defined the mappings before, so it will work as expected.
### Auto API Controllers
You normally create **Controllers** to expose application services as **HTTP API** endpoints. Thus allowing browser or 3rd-party clients to call them via AJAX.
ABP can [**automagically**](../../AspNetCore/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention.
You normally create **Controllers** to expose application services as **HTTP API** endpoints. Thus allowing browser or 3rd-party clients to call them via AJAX. ABP can [**automagically**](../../AspNetCore/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention.
#### Swagger UI
The startup template is configured to run the [swagger UI](https://swagger.io/tools/swagger-ui/) using the [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) library. Run the application and enter `http://localhost:53929/swagger/` as URL on your browser.
The startup template is configured to run the [swagger UI](https://swagger.io/tools/swagger-ui/) using the [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) library. Run the application and enter `https://localhost:XXXX/swagger/` (replace XXXX by your own port) as URL on your browser.
You will see some built-in service endpoints as well as the `Book` service and its REST-style endpoints:
![bookstore-swagger](images/bookstore-swagger.png)
Swagger has a nice UI to test APIs. You can try to execute the `[GET] /api/app/book` API to get a list of books.
### Dynamic JavaScript Proxies
It's common to call HTTP API endpoints via AJAX from the **JavaScript** side. You can use `$.ajax` or another tool to call the endpoints. However, ABP offers a better way.
@ -266,7 +290,7 @@ ABP **dynamically** creates JavaScript **proxies** for all API endpoints. So, yo
#### Testing in the Browser Developer Console
You can easily test the JavaScript proxy using your favorite browser's **Developer Console** now. Run the application again, open your browser's **developer tools** (shortcut: F12), switch to the **Console** tab, type the following code and press enter:
You can easily test the JavaScript proxies using your favorite browser's **Developer Console** now. Run the application, open your browser's **developer tools** (shortcut: F12), switch to the **Console** tab, type the following code and press enter:
````js
acme.bookStore.book.getList({}).done(function (result) { console.log(result); });
@ -275,7 +299,7 @@ acme.bookStore.book.getList({}).done(function (result) { console.log(result); })
* `acme.bookStore` is the namespace of the `BookAppService` converted to [camelCase](https://en.wikipedia.org/wiki/Camel_case).
* `book` is the conventional name for the `BookAppService` (removed AppService postfix and converted to camelCase).
* `getList` is the conventional name for the `GetListAsync` method defined in the `AsyncCrudAppService` base class (removed Async postfix and converted to camelCase).
* `{}` argument is used to send an empty object to the `GetListAsync` method which normally expects an object of type `PagedAndSortedResultRequestDto` which is used to send paging and sorting options to the server.
* `{}` argument is used to send an empty object to the `GetListAsync` method which normally expects an object of type `PagedAndSortedResultRequestDto` that is used to send paging and sorting options to the server (all properties are optional, so you can send an empty object).
* `getList` function returns a `promise`. So, you can pass a callback to the `done` (or `then`) function to get the result from the server.
Running this code produces the following output:
@ -298,7 +322,7 @@ You should see a message in the console something like that:
successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7
````
Check the `books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions too.
Check the `Books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions yourself.
### Create the Books Page
@ -306,20 +330,21 @@ It's time to create something visible and usable! Instead of classic MVC, we wil
Create a new `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project and add a new Razor Page named `Index.cshtml`:
![bookstore-add-index-page](images/bookstore-add-index-page.png)
![bookstore-add-index-page](images/bookstore-add-index-page-v2.png)
Open the `Index.cshtml` and change the content as shown below:
````html
@page
@using Acme.BookStore.Pages.Books
@inherits Acme.BookStore.Pages.BookStorePageBase
@using Acme.BookStore.Web.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@model IndexModel
<h2>Books</h2>
````
* Change the default inhertitance of the Razor View Page Model so it **inherits** from the `BookStorePageBase` class (instead of `PageModel`). The `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages.
* This code changes the default inheritance of the Razor View Page Model so it **inherits** from the `BookStorePageBase` class (instead of `PageModel`). The `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages.
* Ensure that the `IndexModel` (*Index.cshtml.cs)* has the `Acme.BookStore.Pages.Books` namespace, or update it in the `Index.cshtml`.
#### Add Books Page to the Main Menu
@ -334,17 +359,16 @@ context.Menu.AddItem(
#### Localizing the Menu Items
Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain` project:
Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project:
![bookstore-localization-files](images/bookstore-localization-files.png)
![bookstore-localization-files](images/bookstore-localization-files-v2.png)
Open the `en.json` file and add localization texts for `Menu:BookStore` and `Menu:Books` keys:
Open the `en.json` file and add localization texts for `Menu:BookStore` and `Menu:Books` keys to the end of the file:
````json
{
"culture": "en",
"texts": {
//...
"Menu:BookStore": "Book Store",
"Menu:Books": "Books"
}
@ -352,9 +376,9 @@ Open the `en.json` file and add localization texts for `Menu:BookStore` and `Men
````
* ABP's localization system is built on [ASP.NET Core's standard localization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) system and extends it in many ways. See the [localization document](../../Localization.md) for details.
* Localization key names are arbitrary, you can set any name. We prefer to add `Menu` namespace for menu items to distinguish from other texts. If a text is not defined in the localization file, it **fallbacks** to the localization key (ASP.NET Core's standard behavior).
* Localization key names are arbitrary. You can set any name. We prefer to add `Menu:` prefix for menu items to distinguish from other texts. If a text is not defined in the localization file, it **fallbacks** to the localization key (ASP.NET Core's standard behavior).
Run the application and see the menu items are added to the top bar:
Run the application and see the new menu item has been added to the top bar:
![bookstore-menu-items](images/bookstore-menu-items.png)
@ -362,17 +386,16 @@ When you click to the Books menu item, you are redirected to the new Books page.
#### Book List
We will use the [Datatables.net](https://datatables.net/) JQuery plugin to show list of tables on the page. Datatables can completely work via AJAX, so it is fast and provides a good user experience. Datatables plugin is configured in the startup template, so you can directly use it in any page without including any style or script file to your page.
We will use the [Datatables.net](https://datatables.net/) JQuery plugin to show list of tables on the page. Datatables can completely work via AJAX, it is fast and provides a good user experience. Datatables plugin is configured in the startup template, so you can directly use it in any page without including any style or script file to your page.
##### Index.cshtml Changes
##### Index.cshtml
Change the `Pages/Books/Index.cshtml` as following:
````html
@page
@using Acme.BookStore.Pages.Books
@inherits Acme.BookStore.Pages.BookStorePageBase
@model IndexModel
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@model Acme.BookStore.Web.Pages.Books.IndexModel
@section scripts
{
<abp-script src="/Pages/Books/index.js" />
@ -405,7 +428,7 @@ Change the `Pages/Books/Index.cshtml` as following:
Create `index.js` JavaScript file under the `Pages/Books/` folder:
![bookstore-index-js-file](images/bookstore-index-js-file.png)
![bookstore-index-js-file](images/bookstore-index-js-file-v2.png)
`index.js` content is shown below:
@ -425,7 +448,7 @@ $(function () {
````
* `abp.libs.datatables.createAjax` is a helper function to adapt ABP's dynamic JavaScript API proxies to Datatable's format.
* `abp.libs.datatables.normalizeConfiguration` is another helper function. There's no requirment to use it, but it simplifies the datatables configuration by providing conventional values for missing options.
* `abp.libs.datatables.normalizeConfiguration` is another helper function. There's no requirement to use it, but it simplifies the datatables configuration by providing conventional values for missing options.
* `acme.bookStore.book.getList` is the function to get list of books (you have seen it before).
* See [Datatable's documentation](https://datatables.net/manual/) for more configuration options.

68
docs/en/Tutorials/AspNetCore-Mvc/Part-II.md

@ -2,13 +2,13 @@
### About this Tutorial
This is the second part of the tutorial series. See all parts:
This is the second part of the ASP.NET Core MVC tutorial series. See all parts:
* [Part I: Create the project and a book list page](Part-I.md)
* **Part II: Create, Update and Delete books (this tutorial)**
* [Part III: Integration Tests](Part-III.md)
You can download the **source code** of the application [from here](https://github.com/volosoft/abp/tree/master/samples/BookStore).
You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore).
### Creating a New Book
@ -20,7 +20,7 @@ In this section, you will learn how to create a new modal dialog form to create
Create a new razor page, named `CreateModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project:
![bookstore-add-create-dialog](images/bookstore-add-create-dialog.png)
![bookstore-add-create-dialog](images/bookstore-add-create-dialog-v2.png)
##### CreateModal.cshtml.cs
@ -30,7 +30,7 @@ Open the `CreateModal.cshtml.cs` file (`CreateModalModel` class) and replace wit
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Acme.BookStore.Pages.Books
namespace Acme.BookStore.Web.Pages.Books
{
public class CreateModalModel : BookStorePageModelBase
{
@ -63,9 +63,9 @@ Open the `CreateModal.cshtml` file and paste the code below:
````html
@page
@inherits Acme.BookStore.Pages.BookStorePageBase
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Pages.Books.CreateModalModel
@model Acme.BookStore.Web.Pages.Books.CreateModalModel
@{
Layout = null;
}
@ -83,7 +83,7 @@ Open the `CreateModal.cshtml` file and paste the code below:
* This modal uses `abp-dynamic-form` tag helper to automatically create the form from the `CreateBookViewModel` class.
* `abp-model` attribute indicates the model object, the `Book` property in this case.
* `data-ajaxForm` attribute makes the form submitting via AJAX, instead of a classic page post.
* `abp-form-content` tag helper is a placeholder to render the form controls (this is optional and needed only if you added some other content in the `abp-dynamic-form` tag, just like in this view).
* `abp-form-content` tag helper is a placeholder to render the form controls (this is optional and needed only if you added some other content in the `abp-dynamic-form` tag, just like in this page).
#### Add the "New book" Button
@ -138,12 +138,12 @@ Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder
Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code:
````C#
````csharp
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Acme.BookStore.Pages.Books
namespace Acme.BookStore.Web.Pages.Books
{
public class EditModalModel : BookStorePageModelBase
{
@ -153,7 +153,7 @@ namespace Acme.BookStore.Pages.Books
[BindProperty]
public CreateUpdateBookDto Book { get; set; }
private readonly IBookAppService _bookAppService;
public EditModalModel(IBookAppService bookAppService)
@ -180,38 +180,26 @@ namespace Acme.BookStore.Pages.Books
* Mapped `BookDto` (received from the `BookAppService.GetAsync`) to `CreateUpdateBookDto` in the `GetAsync` method.
* The `OnPostAsync` simply uses `BookAppService.UpdateAsync` to update the entity.
#### CreateUpdateBookDto
#### BookDto to CreateUpdateBookDto Mapping
In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, change the `CreateUpdateBookDto` class as shown below:
In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and change it as shown below:
````C#
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
````csharp
using AutoMapper;
namespace Acme.BookStore
namespace Acme.BookStore.Web
{
[AutoMapTo(typeof(Book))]
[AutoMapFrom(typeof(BookDto))]
public class CreateUpdateBookDto
public class BookStoreWebAutoMapperProfile : Profile
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
public DateTime PublishDate { get; set; }
[Required]
public float Price { get; set; }
public BookStoreWebAutoMapperProfile()
{
CreateMap<BookDto, CreateUpdateBookDto>();
}
}
}
````
* Just added the `[AutoMapFrom(typeof(BookDto))]` attribute to create the mapping.
* Just added `CreateMap<BookDto, CreateUpdateBookDto>();` as the mapping definition.
#### EditModal.cshtml
@ -219,8 +207,8 @@ Replace `EditModal.cshtml` content with the following content:
````html
@page
@inherits Acme.BookStore.Pages.BookStorePageBase
@using Acme.BookStore.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@using Acme.BookStore.Web.Pages.Books
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel
@{
@ -231,7 +219,7 @@ Replace `EditModal.cshtml` content with the following content:
<abp-modal-header title="@L["Update"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id" />
<abp-form-content/>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
@ -240,7 +228,7 @@ Replace `EditModal.cshtml` content with the following content:
This page is very similar to the `CreateModal.cshtml` except;
* It includes an `abp-input` for the `Id` property to store id of the editing book.
* It includes an `abp-input` for the `Id` property to store id of the editing book (which is a hidden input).
* It uses `Books/EditModal` as the post URL and *Update* text as the modal header.
#### Add "Actions" Dropdown to the Table
@ -420,6 +408,12 @@ $(function () {
});
````
Open the `en.json` in the `Acme.BookStore.Domain.Shared` project and add the following line:
````json
"BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?"
````
Run the application and try to delete a book.
### Next Part

87
docs/en/Tutorials/AspNetCore-Mvc/Part-III.md

@ -2,22 +2,21 @@
### About this Tutorial
This is the third part of the tutorial series. See all parts:
This is the third part of the ASP.NET Core MVC tutorial series. See all parts:
- [Part I: Create the project and a book list page](Part-I.md)
- [Part II: Create, Update and Delete books](Part-II.md)
- **Part III: Integration Tests (this tutorial)**
You can download the **source code** of the application [from here](https://github.com/volosoft/abp/tree/master/samples/BookStore).
You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore).
### Test Projects in the Solution
There are two test projects in the solution:
There are multiple test projects in the solution:
* `Acme.BookStore.Application.Tests` is for unit & integration tests. You can write tests for application service methods. It uses **EF Core SQLite in-memory** database.
* `Acme.BookStore.Web.Tests` is for full stack integration tests including the web layer. So, you can write tests for UI pages too.
![bookstore-test-projects-v2](images/bookstore-test-projects-v2.png)
Test projects use the following libraries for testing:
Each project is used to test the related application project. Test projects use the following libraries for testing:
* [xunit](https://xunit.github.io/) as the main test framework.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) as an assertion library.
@ -25,79 +24,40 @@ Test projects use the following libraries for testing:
### Adding Test Data
Startup template contains the `BookStoreTestDataBuilder` class in the `Acme.BookStore.Application.Tests` project that creates some data to run tests on. It's shown below:
Startup template contains the `BookStoreTestDataSeedContributor` class in the `Acme.BookStore.TestBase` project that creates some data to run tests on.
````C#
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Acme.BookStore
{
public class BookStoreTestDataBuilder : ITransientDependency
{
private readonly IIdentityDataSeeder _identityDataSeeder;
public BookStoreTestDataBuilder(IIdentityDataSeeder identityDataSeeder)
{
_identityDataSeeder = identityDataSeeder;
}
public void Build()
{
AsyncHelper.RunSync(BuildInternalAsync);
}
public async Task BuildInternalAsync()
{
await _identityDataSeeder.SeedAsync("1q2w3E*");
}
}
}
````
* It simply uses `IIdentityDataSeeder` which is implemented by the identity module and creates an admin role and admin user. You can use them in your tests.
* You can add new test data in the `BuildInternalAsync` method.
Change the `BookStoreTestDataBuilder` class as show below:
Change the `BookStoreTestDataSeedContributor` class as show below:
````C#
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
using Volo.Abp.Guids;
namespace Acme.BookStore
{
public class BookStoreTestDataBuilder : ITransientDependency
public class BookStoreTestDataSeedContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IIdentityDataSeeder _identityDataSeeder;
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreTestDataBuilder(
IIdentityDataSeeder identityDataSeeder,
IRepository<Book, Guid> bookRepository)
public BookStoreTestDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
{
_identityDataSeeder = identityDataSeeder;
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public void Build()
{
AsyncHelper.RunSync(BuildInternalAsync);
}
public async Task BuildInternalAsync()
public async Task SeedAsync(DataSeedContext context)
{
await _identityDataSeeder.SeedAsync("1q2w3E*");
await _bookRepository.InsertAsync(
new Book
{
Id = Guid.NewGuid(),
Id = _guidGenerator.Create(),
Name = "Test book 1",
Type = BookType.Fantastic,
PublishDate = new DateTime(2015, 05, 24),
@ -108,7 +68,7 @@ namespace Acme.BookStore
await _bookRepository.InsertAsync(
new Book
{
Id = Guid.NewGuid(),
Id = _guidGenerator.Create(),
Name = "Test book 2",
Type = BookType.Science,
PublishDate = new DateTime(2014, 02, 11),
@ -120,7 +80,8 @@ namespace Acme.BookStore
}
````
* Injected `IRepository<Book, Guid>` and used it in the `BuildInternalAsync` to create two book entities.
* Injected `IRepository<Book, Guid>` and used it in the `SeedAsync` to create two book entities as the test data.
* Used `IGuidGenerator` service to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases (see the [Guid generation document](../../Guid-Generation.md) for more).
### Testing the BookAppService
@ -210,6 +171,8 @@ public async Task Should_Not_Create_A_Book_Without_Name()
* Since the `Name` is empty, ABP throws an `AbpValidationException`.
### Testing Web Pages
Open the **Test Explorer Window** (use Test -> Windows -> Test Explorer menu if it is not visible) and **Run All** tests:
![bookstore-appservice-tests](images/bookstore-appservice-tests.png)
TODO
Congratulations, green icons show that tests have been successfully passed!

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

3
docs/en/Value-Types.md

@ -0,0 +1,3 @@
## Value Types
TODO

4
docs/en/docs-nav.json

@ -41,6 +41,10 @@
}
]
},
{
"text": "CLI",
"path": "CLI.md"
},
{
"text": "Fundamentals",
"items": [

BIN
docs/en/images/bookstore-user-management-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/images/bookstore-user-management.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/en/images/bookstore-visual-studio-solution-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/en/images/bookstore-visual-studio-solution.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/en/images/db-migrator-app.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

4
docs/zh-Hans/AspNetCore/Auto-API-Controllers.md

@ -16,7 +16,9 @@ public class BookStoreWebModule : AbpModule
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly);
options
.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly);
});
}
}

2
docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md

@ -5,4 +5,4 @@
* **推荐** 定义 **public getter 和 setter** 的DTO成员 .
* **推荐** 使用 **data annotations** **验证** service输入DTO的属性.
* **不推荐** 在DTO中添加任何 **逻辑**, 在必要的时候可以实现 `IValidatableObject` 接口.
* **推荐** 为所有的DTO标记 **[Serializable]** Attribute. 因为它们已经是可序列化的, 开人发员可能会希望进行二进制序列化.
* **推荐** 为所有的DTO标记 **[Serializable]** Attribute. 因为它们已经是可序列化的, 开发人员可能会希望进行二进制序列化.

4
docs/zh-Hans/Best-Practices/Entities.md

@ -64,7 +64,7 @@ public class Issue : FullAuditedAggregateRoot<Guid> //使用Guid作为键/标识
protected Issue()
{
/* 此构造函数是提供给ORM用来从数据库中获取实体.
* - 无需初始化Lanels集合
* - 无需初始化Labels集合
因为它会被来自数据库的值覆盖.
- It's protected since proxying and deserialization tools
可能不适用于私有构造函数.
@ -150,4 +150,4 @@ public class IssueLabel : Entity
### 参考文献
* Effective Aggregate Design by Vaughn Vernon
http://dddcommunity.org/library/vernon_2011
http://dddcommunity.org/library/vernon_2011

137
docs/zh-Hans/CLI.md

@ -0,0 +1,137 @@
# ABP CLI
ABP CLI (Command Line Interface) is a command line tool to perform some common operations for ABP based solutions.
## Installation
ABP CLI is a [dotnet global tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools). Install it using a command line window:
````bash
dotnet tool install -g Volo.Abp.Cli
````
To update an existing installation:
````bash
dotnet tool update -g Volo.Abp.Cli
````
## Commands
### new
Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md).
Basic usage:
````bash
abp new <solution-name> [options]
````
Example:
````bash
abp new Acme.BookStore
````
* Acme.BookStore is the solution name here.
* Common convention is to name a solution is like *YourCompany.YourProject*. However, you can use different naming like *YourProject* (single level namespacing) or *YourCompany.YourProduct.YourModule* (three levels namespacing).
#### Options
* `--template` or `-t`: Specifies the template name. Default template name is `mvc`. Available templates:
* `mvc` (default): ASP.NET Core [MVC application template](Startup-Templates/Mvc.md). Additional options:
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
* `mvc-module`: ASP.NET Core [MVC module template](Startup-Templates/Mvc-Module.md). Additional options:
* `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI).
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
### add-package
Adds a new ABP package to a project by,
* Adding related nuget package as a dependency to the project.
* Adding `[DependsOn(...)]` attribute to the module class in the project (see the [module development document](Module-Development-Basics.md)).
> Notice that the added module may require additional configuration which is generally indicated in the documentation of the related package.
Basic usage:
````bash
abp add-package <package-name> [options]
````
Example:
````
abp add-package Volo.Abp.MongoDB
````
* This example adds the Volo.Abp.MongoDB package to the project.
#### Options
* `--project` or `-p`: Specifies the project (.csproj) file path. If not specified, CLI tries to find a .csproj file in the current directory.
### add-module
Adds a multi-package module to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution.
> A business module generally consists of several packages (because of layering, different database providr options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module.
Basic usage:
````bash
abp add-module <module-name> [options]
````
Example:
```bash
abp add-module Volo.Blogging
```
* This example add the Volo.Blogging module to the solution.
#### Options
* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory.
* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation.
### update
Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related packages in a solution or project to the latest versions.
Usage:
````bash
abp update [options]
````
* If you run in a directory with a .sln file, it updates all ABP related packages of the all projects of the solution to the latest versions.
* If you run in a directory with a .csproj file, it updates all ABP related packages of the project to the latest versions.
#### Options
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions.
### help
Writes basic usage information of the CLI.
Usage:
````bash
abp help [command-name]
````
Examples:
````bash
abp help # Shows a general help.
abp help new # Shows help about the "new" command.
````

3
docs/zh-Hans/Data-Seeding.md

@ -0,0 +1,3 @@
# Data Seeding
TODO

2
docs/zh-Hans/Exception-Handling.md

@ -70,7 +70,7 @@ ABP提供了用于处理Web应用程序异常的标准模型.
"code": "App:010046",
"message": "Your request is not valid, please correct and try again!",
"validationErrors": [{
"message": "Username should be minimum lenght of 3.",
"message": "Username should be minimum length of 3.",
"members": ["userName"]
},
{

6
docs/zh-Hans/Getting-Started-AspNetCore-Application.md

@ -77,11 +77,11 @@ namespace BasicAspNetCoreApplication
{
public class Startup
{
public IServiceProvider ConfigureServices(ServiceConfigurationContext context)
public IServiceProvider ConfigureServices(IServiceCollection services)
{
context.Services.AddApplication<AppModule>();
services.AddApplication<AppModule>();
return context.Services.BuildServiceProviderFromFactory();
return services.BuildServiceProviderFromFactory();
}
public void Configure(IApplicationBuilder app)

69
docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md

@ -2,37 +2,42 @@
### 创建新项目
访问[模板创建页面](https://abp.io/Templates), 输入项目名称并创建项目, 如下所示:
本教程使用 **ABP CLI** 创建一个新项目. 更多选项, 请参阅[入门](https://cn.abp.io/get-started)页面.
![bookstore-create--template](images/bookstore-create-template.png)
如果你之前未安装,请使用命令行安装ABP CLI:
单击 *create* 按钮时,将使用你提供的名称创建一个新的Visual Studio解决方案并开始下载.
````bash
dotnet tool install -g Volo.Abp.Cli
````
在空文件夹中使用 `abp new` 命令来创建项目:
````
abp new Acme.BookStore
````
> 你可以使用不同级别的命名空间; 例如BookStore, Acme.BookStore或Acme.Retail.BookStore.
`new` 命令创建**分层MVC应用程序**, **Entity Framework Core**作为数据库提供程序. 但是,它还有其他选择. 有关所有可用选项,请参见[CLI文档](CLI.md)
#### 预先要求
下载的项目需要:
创建项目的要求:
* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/)
* [.NET Core 2.1.1+](https://www.microsoft.com/net/download/dotnet-core/)
* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/)
### 解决方案结构
下载后解压文件并在 **Visual Studio 2017(15.7.0 +)** 中打开:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
该解决方案具有分层结构(基于域驱动设计), 其中:
在**Visual Studio**中打开解决方案:
* ``.Domain`` 为领域层.
* ``.Application`` 为应用层.
* ``.Web`` 为是表示层.
* ``.EntityFrameworkCore`` 是EF Core集成.
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png)
EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项目中.
该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目,可与**EF Core**和**SQLite**数据库内存一起使用.
解决方案还包含配置好的的单元&集成测试项目, 以便与 **EF Core** 和 **SQLite内存中** 数据库配合使用.
> 请参阅[MVC应用程序模板文档](Startup-Templates/Mvc.md)以详细了解解决方案结构.
### 创建数据库
### 数据库连接字符串
查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**:
@ -44,9 +49,29 @@ EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项
}
````
解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS.
解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串.
### 创建数据库并应用数据库迁移
你有两个选项来创建数据库.
右键单击`.Web`项目并**设置启动项目**
#### 使用DbMigrator应用程序
该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用.
> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串.
右键单击`.DbMigrator`项目并选择 **设置为启动项目**:
![set-as-startup-project](images/set-as-startup-project.png)
按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出:
![set-as-startup-project](images/db-migrator-app.png)
#### 使用EF Core Update-Database命令
Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**:
![set-as-startup-project](images/set-as-startup-project.png)
@ -56,6 +81,8 @@ EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项
这将基于配置的连接字符串创建新数据库.
> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序.
### 运行应用程序
你现在可以运行应用程序,它将会打开**home**页面:
@ -64,9 +91,9 @@ EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项
单击 **登录** 按钮, 输入用户名`admin`, 密码`1q2w3E*`, 登录应用程序.
启动模板包括 **身份管理(identity management)** 模块. 登录后将提供身份管理菜单,你可以在其中管理**角色**,**用户**及其**权限**.
启动模板包括**身份管理**和**租户管理**模块. 登录后,将显示"管理"菜单, 你可以在其中管理**租户**,**角色**,**用户**和**权限**. 用户管理页面如下所示:
![bookstore-user-management](images/bookstore-user-management.png)
![bookstore-user-management](images/bookstore-user-management-v2.png)
### 下一步是什么?

92
docs/zh-Hans/Index.md

@ -1,68 +1,32 @@
# ABP 文档
> 翻译来自[cnAbp](https://github.com/cnabp)组织,中文网会持续跟进翻译,目前Abp vNext的英文文档还未完成,大家对整体框架没有深入的理解,翻译难免存在一些问题.敬请见谅.😀
> 中文文档翻译来自[cnAbp](https://github.com/cnabp)组织,Abp中文网会持续跟进翻译,目前Abp vNext的英文文档还未完成,大家对整体框架没有深入的理解,翻译难免存在一些问题.敬请见谅.😀
## 目录
ABP是一个**开源应用程序框架**,专注于基于ASP.NET Core的Web应用程序开发,但也支持开发其他类型的应用程序.
* 入门
* 从启动模板开始
* [ASP.NET Core MVC 模板](Getting-Started-AspNetCore-MVC-Template.md)
* 从空项目开始
* [使用Console Application](Getting-Started-Console-Application.md)
* [使用 ASP.NET Core Web Application](Getting-Started-AspNetCore-Application.md)
* 教程
* 应用开发
* [使用 ASP.NET Core MVC](Tutorials/AspNetCore-Mvc/Part-I.md)
* 基础知识
* [依赖注入](Dependency-Injection.md)
* AutoFac 集成
* [虚拟文件系统](Virtual-File-System.md)
* [本地化](Localization.md)
* [异常处理](Exception-Handling.md)
* 验证
* 授权
* 缓存
* 审计
* 设置管理
* 对象映射
* AutoMapper 集成
* 事件
* 本地 Event Bus
* 分布式 Event Bus
* RabbitMQ 集成
* 服务
* 对象序列化
* JSON序列化
* 邮件
* GUIDs
* 线程
* 定时
* [多租户](Multi-Tenancy.md)
* 模块开发
* [基础](Module-Development-Basics.md)
* 模块插件
* [最佳实践](Best-Practices/Index.md)
* 领域驱动设计
* 领域层
* [实体&聚合根](Entities.md)
* 值对象
* [仓储](Repositories.md)
* 领域服务
* 规约
* 应用服务层
* 应用服务
* 数据传输对象(DTO)
* 工作单元
* ASP.NET Core MVC
* API 版本控制
* 用户界面
* [客户端包管理](AspNetCore/Client-Side-Package-Management.md)
* [捆绑&压缩](AspNetCore/Bundling-Minification.md)
* [Tag Helpers](Tag-Helpers.md)
* [主题](AspNetCore/Theming.md)
* 后台服务
* [后台作业](Background-Jobs.md)
* 数据访问
* [Entity Framework Core 集成](Entity-Framework-Core.md)
* [MongoDB 集成](MongoDB.md)
* 测试
浏览左侧导航菜单以深入了解文档.
## 项目状态
ABP是开源[ASP.NET Boilerplate](https://aspnetboilerplate.com/)框架的**下一代框架**, 它目前处于早期预览阶段,尚未准备好在生产中使用. 文档仍在进行中,远未完成.
对于短期和生产级应用程序, 建议使用[ASP.NET Boilerplate](https://aspnetboilerplate.com/)框架,该框架具有丰富的功能集,成熟,积极维护和最新.
## 入门
使用ABP开发新项目的最简单方法是使用启动模板:
* [ASP.NET Core MVC 模板](Getting-Started-AspNetCore-MVC-Template.md)
如果您想从头开始(使用空项目),请手动安装ABP框架并使用以下教程:
* [控制台应用程序](Getting-Started-Console-Application.md)
* [ASP.NET Core Web 应用程序](Getting-Started-AspNetCore-Application.md)
## 源码
ABP托管在GitHub上, 参见[源代码](https://github.com/abpframework/abp).
## 贡献代码
ABP是一个社区驱动的开源项目.如果你想成为该项目的一部分,请参阅[贡献指南](Contribution/Index.md).

37
docs/zh-Hans/Samples/Microservice-Demo.md

@ -116,11 +116,29 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服
* 在`samples\MicroserviceDemo`文件夹中的命令行运行`dotnet restore`命令.
* 在Visual Studio中构建解决方案.
#### 还原数据库
#### 创建数据库
在`samples\MicroserviceDemo\databases`文件夹中打开`MsDemo_Identity.zip`和`MsDemo_ProductManagement.zip`并恢复到SQL Server.
MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库的结构。其实你可以很轻松的创建数据库,因为这个解决方案配置了使用 Entity Core Code First 来做迁移。
> 请注意:这些数据库在解决方案中具有EF Core迁移,但它们没有种子数据,尤其是IdentityServer4所需的配置. 因此,恢复数据库要容易得多.
这个解决方案中有两个 SQL server 数据库。
##### MsDemo_Identity 数据库
* 右键 `AuthServer.Host` 项目,然后点击 `设置为启动项目`.
* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台)
* 选择 `AuthServer.Host` 成为 **默认项目**.
* 执行 `Update-Database` 命令.
![microservice-sample-update-database-authserver](../images/microservice-sample-update-database-authserver.png)
##### MsDemo_ProductManagement
* 右键 `ProductService.Host` 项目,然后点击 `设置为启动项目`.
* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台)
* 选择 `ProductService.Host` 成为 **默认项目**.
* 执行 `Update-Database` 命令.
![microservice-sample-update-database-products](../images/microservice-sample-update-database-products.png)
#### 运行项目
@ -148,7 +166,7 @@ Visual Studio解决方案由多个项目组成,每个项目在系统中具有不
- **AuthServer.Host**: 托管IdentityServer4以向其他服务和应用程序提供身份验证服务. 它是一个单点登录服务器,包含登录页面.
- **BackendAdminApp.Host**: 这是一个后端管理应用程序,用于托管身份和产品管理模块的UI.
- **PubicWebSite.Host**: 作为包含简单产品列表页面和博客模块UI的公共网站.
- **PublicWebSite.Host**: 作为包含简单产品列表页面和博客模块UI的公共网站.
- **ConsoleClientDemo**: 一个简单的控制台应用程序,用于演示C#应用程序中使用服务.
### 网关/BFF(前端后端)
@ -276,8 +294,7 @@ context.Services.AddAuthentication(options =>
* 它将"Cookies"身份验证添加为主要身份验证类型.
* "oidc"身份验证配置为使用AuthServer应用程序作为身份验证服务器.
* 它需要额外的身份范围(scopes) *role*, *email* and *phone*.
* It requires the API resource scopes *BackendAdminAppGateway*, *IdentityService* and *ProductService* because it will use these services as APIs.
* 它需要额外的身份范围(scopes) *role*, *email* 和 *phone*.
* 它需要API资源范围 *BackendAdminAppGateway*, *IdentityService* 和 *ProductService*,因为它将这些服务用作API.
IdentityServer客户端设置存储在`appsettings.json`文件中:
@ -577,8 +594,6 @@ Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的
`ReRoutes`是一个URL映射数组. `GlobalConfiguration`部分中的`BaseUrl`是该网关的URL(Ocelot需要知道自己的URL). 参见 [ocelot文档](https://ocelot.readthedocs.io/en/latest/features/configuration.html) 更好地了解配置.
Ocelot is a finalizer ASP.NET Core middleware and should be written as the last item in the pipeline:
Ocelot是一个终结ASP.NET核心中间件,应该写成管道中的最后一项:
````csharp
@ -935,7 +950,7 @@ context.Services.AddAuthentication("Bearer")
#### IdentityServer Client
此微服务还通过内部网关使用Identity微服务API, 因为在某些情况下它需要查询用户详细信息(username, email, phone, name and surname). 因此,它也是IdentityServer的客户端,并在`appsettings.json`文件中定义了一个部分:
此微服务还通过内部网关使用Identity微服务API, 因为在某些情况下它需要查询用户详细信息(username, email, phone, name 和 surname). 因此,它也是IdentityServer的客户端,并在`appsettings.json`文件中定义了一个部分:
````json
"IdentityClients": {
@ -1334,7 +1349,7 @@ public async Task<ProductDto> UpdateAsync(Guid id, UpdateProductDto input)
AddDistributedEvent(new ProductStockCountChangedEto(Id, StockCount, stockCount));
````
`ProductStockCountChangedEto` was defined as shown below:
`ProductStockCountChangedEto` 定义如下:
````csharp
[Serializable]
@ -1458,4 +1473,4 @@ ABP提供自动审计日志记录,详细保存每个请求(当前用户,浏览
所有服务和应用程序都配置为编写审核日志. 审核日志将保存到MsDemo_Identity SQL数据库中. 因此,您可以从单个点查询所有应用程序的所有审核日志.
审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表.
审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表.

8
docs/zh-Hans/Startup-Templates/Index.md

@ -0,0 +1,8 @@
# 启动模板
虽然你可以从一个空项目开始并手动添加所需的包,但启动模板可以非常轻松,舒适地使用ABP框架启动新的解决方案.
单击下面列表中的名称以查看相关启动模板的文档:
* [**mvc**](Mvc.md): ASP.NET Core MVC应用程序模板.
* [**mvc-module**](Mvc-Module.md): ASP.NET Core MVC模块/服务模板.

6
docs/zh-Hans/Startup-Templates/Mvc-Module.md

@ -0,0 +1,6 @@
# MVC Module 启动模板
TODO

183
docs/zh-Hans/Startup-Templates/Mvc.md

@ -0,0 +1,183 @@
# MVC Application Startup Template
## Introduction
This template provides a layered (or tiered, based on the preference) application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices.
## How to Start With
You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here.
First, install the ABP CLI as described in [its document](../CLI.md). Then use the `abp new` command in an empty folder to create a new solution:
````bash
abp new Acme.BookStore -t mvc
````
* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. See the [CLI document](../CLI.md) for different naming styles.
* This example specified the template name (`-t` or `--template` option). However, `mvc` is the default template and used even if you don't specify it.
### Specify Database Provider
This template supports the following database providers:
- `ef`: Entity Framework Core (default)
- `mongodb`: MongoDB
Use the `-d` (or `--database-provider`) to specify the database provider:
````bash
abp new Acme.BookStore -t mvc -d mongodb
````
### Create a Tiered Solution
`--tiered` option is used to create a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
````bash
abp new Acme.BookStore --tiered
````
See the "Tiered Structure" section below for the tiered approach.
## Solution Structure
Based on the options you've specified, you will get a slightly different solution structure.
### Default Structure
If you don't specify any option, you will have a solution like shown below:
![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png)
Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before.
--------------------
**TODO: Add a graphic to illustrate dependencies between projects.**
------------------
Each section below will explain the related project.
#### .Domain Project
This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects of the solution.
A `Book` entity and a `IBookRepository` interface are good candidates for this project.
* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project.
#### .Domain.Shared Project
This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution.
A `BookType` enum and a `BookConts` class (which may have some constant fields for the `Book` entity, like `MaxNameLength`) are good candidates for this project.
This project has no dependency to other projects in the solution.
#### .Application.Contracts Project
This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package.
* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects in the application service interfaces and DTOs.
#### .Application Project
This project contains the [application service](../Application-Services.md) implementations of the interfaces defined in the `.Application.Contracts` project.
* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs.
* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic.
#### .EntityFrameworkCore Project.
This is the integration project for the EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project.
* Depends on the `.Domain` project to be able to reference to entities and repository interfaces.
> This project is available only if you are using EF Core as the database provider.
#### .EntityFrameworkCore.DbMigrations Project
Contains EF Cor database migrations for the solution. It has a separated `DbContext` to dedicated to manage migrations.
ABP is a modular framework and with an ideal design, each module has its own `DbContext` class. This is where the migration `DbContext` comes into play and unifies all `DbContext` configurations into a single model to maintain a single database schema.
Notice that the migration `DbContext` is only used for database migrations and *not used on runtime*.
* Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application.
> This project is available only if you are using EF Core as the database provider.
#### .DbMigrator Project
This is a console application which simplifies to execute database migrations on development and production environments. When you this application;
* Creates the database if necessary.
* Applies database migrations.
* Seeds initial data.
> This project has its own `appsettings.json` file. So, if you want to change the database connection string, remember to change this file.
Especially, seeding initial data is important at this point. ABP has a modular data seed infrastructure. See [its documentation](../Data-Seeding.md) for more about the data seeding.
While creating database & applying migrations seems only necessary for relational databases, this projects comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds initial data which is necessary for the application.
* Depends on the `.EntityFrameworkCore.DbMigrations` project (for EF Core) since it needs to access to the migrations.
* Depends on the `.Application.Contracts` project to be able to access permission definitions, because initial data seeder grants permissions for the admin user.
#### .HttpApi Project
This project is used to define your API Controllers.
Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it.
* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces.
#### .HttpApi.Client Project
This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications.
`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies.
Most of time you don't need to manually create C# client proxies, thanks to ABP's [Cynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature.
* Depends on the `.Application.Contracts` project to be able to share the same application service interfaces and DTOs with the remote service.
#### .Web Project
This project contains the User Interface (UI) of the application. It contains razor pages, javascript files, css files, images and so on...
* Depends on the `.HttpApi` since UI layer needs to use APIs and application service interfaces of the solution.
> If you check the source code of the `.Web.csproj` file, you will see the references to the `.Application` and the `.EntityFrameworkCore.DbMigrations` projects.
>
> These references are actually not needed on development, because UI layer normally doesn't depend on the EF Core or the Application implementation. This startup templates are ready for the tiered deployment, where API layer is hosted in a separate server than the UI layer.
>
> However, if you don't choose the `--tiered` option, these references will be in the .Web project to be able to host the Web, API and application layers in a single application endpoint.
>
> This gives you to ability to use domain entities in your presentation layer. However, this is considered as a bad practice according to the DDD.
#### Test Projects
The solution has multiple test projects, one for each layer:
* `.Domain.Tests` is used to test the domain layer.
* `.Application.Tests` is used to test the application layer.
* `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories.
* `.Web.Tests` is used to test the UI.
* `.TestBase` is a base (shared) project for all tests.
In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application.
### Tiered Structure
TODO
### Other Database Providers
TODO
#### MongoDB
TODO

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

@ -2,7 +2,7 @@
### 关于本教程
本教程中,你会创建一个用于管理书籍和书籍作者的程序.会用到 **Entity Framework Core** (EF Core)作为ORM([启动模板](https://abp.io/Templates)中预配置的ORM).
在本系列教程中, 你将构建一个用于管理书籍及其作者列表的应用程序. **Entity Framework Core**(EF Core)将用作ORM提供者,因为它是默认数据库提供者.
这是本教程所有章节中的第一章,下面是所有的章节:
@ -10,35 +10,37 @@
- [Part II: 创建,编辑,删除书籍](Part-II.md)
- [Part III: 集成测试](Part-III.md)
你可以从[这里](https://github.com/volosoft/abp/tree/master/samples/BookStore)下载本程序的源码.
你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**.
### 创建项目
打开[启动模板页](https://abp.io/Templates)并下载一个新的项目叫做`Acme.BookStore`.根据[模板文档](../../Getting-Started-AspNetCore-MVC-Template.md)创建数据库并运行这个程序.
创建一个名为`Acme.BookStore`的新项目, 创建数据库并按照[入门文档](../../Getting-Started-AspNetCore-MVC-Template.md)运行应用程序.
### 解决方案的结构
下面的图片展示了从启动模板创建的项目是如何分层的.
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png)
> 你可以查看[MVC应用程序模板文档](../../Startup-Templates/Mvc.md)以详细了解解决方案结构.但是,你将通过本教程了解基础知识.
### 创建Book实体
在 **领域层** 定义[实体](../../Entities.md)(`Acme.BookStore.Domain` 项目中).这个项目最主要的实体就是`Book`:
启动模板中的域层分为两个项目:
- `Acme.BookStore.Domain`包含你的[实体](../../Entities.md), [领域服务](../../Domain-Services.md)和其他核心域对象.
- `Acme.BookStore.Domain.Shared`包含可与客户共享的常量,枚举或其他域相关对象.
在解决方案的**领域层**(`Acme.BookStore.Domain`项目)中定义[实体](../../Entities.md). 该应用程序的主要实体是`Book`. 在`Acme.BookStore.Domain`项目中创建一个名为`Book`的类,如下所示:
````C#
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
[Table("Books")]
public class Book : AuditedAggregateRoot<Guid>
{
[Required]
[StringLength(128)]
public string Name { get; set; }
public BookType Type { get; set; }
@ -50,8 +52,8 @@ namespace Acme.BookStore
}
````
* ABP有两个基本的实体基类: `AggregateRoot` 和 `Entity`.**Aggregate Root**是 **领域驱动设计(DDD)** 的概念之一.更多信息和最佳实践请查看[实体文档](../../Entities.md).
* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些审计属性(`CreationTime`, `CreatorId`, `LastModificationTime`... 等.).
* ABP为实体提供了两个基本的基类: `AggregateRoot`和`Entity`. **Aggregate Root**是**域驱动设计(DDD)** 概念之一. 有关详细信息和最佳做法,请参阅[实体文档](../../Entities.md).
* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些审计属性(`CreationTime`, `CreatorId`, `LastModificationTime` 等).
* `Guid`是`Book`实体的主键类型.
* 使用 **数据注解** 为EF Core添加映射.或者你也可以使用 EF Core 自带的[fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling).
@ -62,10 +64,10 @@ namespace Acme.BookStore
````C#
namespace Acme.BookStore
{
public enum BookType : byte
public enum BookType
{
Undefined,
Advanture,
Adventure,
Biography,
Dystopia,
Fantastic,
@ -91,14 +93,15 @@ public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
#### 配置你的Book实体
从`Acme.BookStore.EntityFrameworkCore` 项目中打开 BookStoreDbContextModelCreatingExtensions.cs 文件, 在 ConfigureBookStore 方法最后添加如下代码来配置Book实体:
在`Acme.BookStore.EntityFrameworkCore`项目中打开`BookStoreDbContextModelCreatingExtensions.cs`文件,并将以下代码添加到`ConfigureBookStore`方法的末尾以配置Book实体:
````C#
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureExtraProperties();
});
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureAuditedAggregateRoot(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
````
#### 添加新的Migration并更新数据库
@ -115,26 +118,27 @@ PM> Update-Database
#### 添加示例数据
`Update-Database`命令会在数据库中创建`Books`表.打开这个表添加几行数据,然后就可以把这些数据展示到页面上:
`Update-Database`命令在数据库中创建了`AppBooks`表. 打开数据库并输入几个示例行,以便在页面上显示它们:
![bookstore-books-table](images/bookstore-books-table.png)
### 创建应用服务
下一步是创建[应用服务](../../Application-Services.md)来管理(创建,列出,更新,删除...)书籍.
下一步是创建[应用服务](../../Application-Services.md)来管理(创建,列出,更新,删除)书籍. 启动模板中的应用程序层分为两个项目:
* `Acme.BookStore.Application.Contracts`主要包含你的DTO和应用程序服务接口.
* `Acme.BookStore.Application`包含应用程序服务的实现.
#### BookDto
在`Acme.BookStore.Application`项目中添加一个名为`BookDto`的DTO类:
在`Acme.BookStore.Application.Contracts`项目中创建一个名为`BookDto`的DTO类:
````C#
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AutoMapper;
namespace Acme.BookStore
{
[AutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
@ -151,7 +155,23 @@ namespace Acme.BookStore
* **DTO**类被用来在 **表示层** 和 **应用层** **传递数据**.查看[DTO文档](../../Data-Transfer-Objects.md)查看更多信息.
* 为了在页面上展示书籍信息,`BookDto`被用来将书籍数据传递到表示层.
* `BookDto`继承自 `AuditedEntityDto<Guid>`.跟上面定义的`Book`类一样具有一些审计属性.
* `[AutoMapFrom(typeof(Book))]`用来创建从`Book`类到`BookDto`的AutoMapper映射.使用这种方法.你可以将`Book`对象自动转换成`BookDto`对象(而不是手动复制所有的属性).
在将书籍返回到表示层时,需要将`Book`实体转换为`BookDto`对象. [AutoMapper](https://automapper.org)库可以在定义了正确的映射时自动执行此转换. 启动模板配置了AutoMapper,因此你只需在`Acme.BookStore.Application`项目的`BookStoreApplicationAutoMapperProfile`类中定义映射:
````csharp
using AutoMapper;
namespace Acme.BookStore
{
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
}
}
}
````
#### CreateUpdateBookDto
@ -184,11 +204,17 @@ namespace Acme.BookStore
````
* 这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息.
* 类中的属性定义了数据注解(如`[Required]`)用来定义有效性验证.ABP会自动校验DTO的数据有效性.
* 它定义了数据注释属性(如`[Required]`)来定义属性的验证. DTO由ABP框架[自动验证](../../Validation.md).
就像上面的`BookDto`一样,创建一个从`CreateUpdateBookDto`对象到`Book`实体的映射:
````csharp
CreateMap<CreateUpdateBookDto, Book>();
````
#### IBookAppService
为应用服务定义一个名为 `IBookAppService` 的接口:
在`Acme.BookStore.Application.Contracts`项目中定义一个名为`IBookAppService`的接口:
````C#
using System;
@ -210,15 +236,14 @@ namespace Acme.BookStore
}
````
* 为应用服务定义接口不是必须的,不过,这是推荐的最佳实践.
* `IAsyncCrudAppService`中定义了基础的 **CRUD**方法:`GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` 和 `DeleteAsync`.不需要扩展它.取而代之,你可以继承空的`IApplicationService`接口定义你自己的方法.
* `IAsyncCrudAppService`有一些变体,你可以为每一个方法使用单个或者多个的DTO.(译者注:意思是类似EntityDto和UpdateEntityDto可以用同一个,也可以分别单独指定
)
* 框架定义应用程序服务的接口<u>不是必需的</u>. 但是,它被建议作为最佳实践.
* `IAsyncCrudAppService`定义了常见的**CRUD**方法:`GetAsync`,`GetListAsync`,`CreateAsync`,`UpdateAsync`和`DeleteAsync`. 你可以从空的`IApplicationService`接口继承并手动定义自己的方法.
* `IAsyncCrudAppService`有一些变体, 你可以在每个方法中使用单独的DTO,也可以分别单独指定.
#### BookAppService
创建 `BookAppService` 并实现 `IBookAppService`接口:
在`Acme.BookStore.Application`项目中实现名为`BookAppService`的`IBookAppService`:
````C#
using System;
@ -243,23 +268,23 @@ namespace Acme.BookStore
````
* `BookAppService`继承了`AsyncCrudAppService<...>`.`AsyncCrudAppService<...>`实现了上面定义的CRUD方法.
* `BookAppService`注入了`IRepository<Book, Guid>`,`IRepository<Book, Guid>`是默认为`Book`创建的仓储.ABP会自动为每一个聚合根(或实体)创建仓储.参考[仓储文档](../../Repositories.md).
* `BookAppService`使用了 `IObjectMapper` 将`Book`转换成`BookDto`,将`CreateUpdateBookDto`转换成`Book`.启动模板中使用了[AutoMapper](http://automapper.org/)作为对象映射提供程序.你可以像上面那样使用`AutoMapFrom` 和 `AutoMapTo`定义映射.查看[AutoMapper集成文档](../../AutoMapper-Integration.md)获取更多信息.
* `BookAppService`注入`IRepository <Book,Guid>`,这是`Book`实体的默认仓储. ABP自动为每个聚合根(或实体)创建默认仓储. 请参阅[仓储文档](../../Repositories.md)
* `BookAppService`使用`IObjectMapper`将`Book`对象转换为`BookDto`对象, 将`CreateUpdateBookDto`对象转换为`Book`对象. 启动模板使用[AutoMapper](http://automapper.org/)库作为对象映射提供程序. 你之前定义了映射, 因此它将按预期工作.
### 自动生成API Controllers
你通常需要创建 **Controllers** 将应用服务暴露为 **HTTP API**.这样浏览器或第三方客户端可以通过AJAX的方式访问它们.
ABP可以通过约定[**自动**](../../AspNetCore/Auto-API-Controllers.md)将应用服务转换成MVC API Controllers.
你通常创建**Controller**以将应用程序服务公开为**HTTP API**端点. 因此允许浏览器或第三方客户端通过AJAX调用它们. ABP可以[**自动**](../../AspNetCore/Auto-API-Controllers.md)按照惯例将你的应用程序服务配置为MVC API控制器.
#### Swagger UI
启动模板使用了[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)库配置了[swagger UI](https://swagger.io/tools/swagger-ui/).运行程序并在浏览器中输入`http://localhost:53929/swagger/`.
启动模板配置为使用[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)运行[swagger UI](https://swagger.io/tools/swagger-ui/). 运行应用程序并在浏览器中输入`https://localhost:XXXX/swagger/`(用您自己的端口替换XXXX)作为URL.
你会看到一些内置的接口和`Book`的接口,它们都是REST风格的:
![bookstore-swagger](images/bookstore-swagger.png)
Swagger有一个很好的UI来测试API. 你可以尝试执行`[GET] /api/app/book` API来获取书籍列表.
### 动态JavaScript代理
在Javascript端通过AJAX的方式调用HTTP API接口是很常见的,你可以使用`$.ajax`或这其他的工具来调用接口.当然,ABP中提供了更好的方式.
@ -270,6 +295,8 @@ ABP **自动** 为所有的API接口创建了JavaScript **代理**.因此,你可
你可以使用你钟爱的浏览器的 **开发者控制台** 中轻松测试JavaScript代理.运行程序,并打开浏览器的 **开发者工具**(快捷键:F12),切换到 **Console** 标签,输入下面的代码并回车:
你现在可以使用自己喜欢的浏览器的**开发者控制台**轻松测试JavaScript代理. 运行应用程序, 打开浏览器的**开发者工具**(快捷键:F12),切换到**Console**选项卡,输入以下代码并按回车键.
````js
acme.bookStore.book.getList({}).done(function (result) { console.log(result); });
````
@ -277,7 +304,7 @@ acme.bookStore.book.getList({}).done(function (result) { console.log(result); })
* `acme.bookStore`是`BookAppService`的命名空间,转换成了[驼峰命名](https://en.wikipedia.org/wiki/Camel_case).
* `book`是`BookAppService`转换后的名字(去除了AppService后缀并转成了驼峰命名).
* `getList`是定义在`AsyncCrudAppService`基类中的`GetListAsync`方法转换后的名字(去除了Async后缀并转成了驼峰命名).
* `{}`参数用来传递一个空的对象给`GetListAsync`方法.GetListAsync期望的参数是`PagedAndSortedResultRequestDto`类型的对象,`PagedAndSortedResultRequestDto`类型中定义了分页和排序选项.
* `{}`参数用于将空对象发送到`GetListAsync`方法,该方法通常需要一个类型为`PagedAndSortedResultRequestDto`的对象,用于向服务器发送分页和排序选项(所有属性都是可选的,所以你可以发送一个空对象).
* `getList`方法返回了一个`promise`.因此,你可以传递一个回调函数到`done`(或者`then`)方法中来获取服务返回的结果.
运行这段代码会产生下面的输出:
@ -300,7 +327,7 @@ acme.bookStore.book.create({ name: 'Foundation', type: 7, publishDate: '1951-05-
successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7
````
检查数据库表`books`中的数据,你会发现多了一行新数据,你也可以尝试`get`, `update` 和 `delete`方法.
检查数据库中的`Books`表以查看新书. 你可以自己尝试`get`,`update`和`delete`功能.
### 创建书籍页面
@ -309,20 +336,21 @@ successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7
在 `Acme.BookStore.Web`项目的`Pages`文件夹下创建一个新的文件夹叫`Books`并添加一个名为`Index.cshtml`的Razor Page.
![bookstore-add-index-page](images/bookstore-add-index-page.png)
![bookstore-add-index-page](images/bookstore-add-index-page-v2.png)
打开`Index.cshtml`并把内容修改成下面这样:
````html
@page
@using Acme.BookStore.Pages.Books
@inherits Acme.BookStore.Pages.BookStorePageBase
@using Acme.BookStore.Web.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@model IndexModel
<h2>Books</h2>
````
* 修改Razor View Page Model的默认继承,使页面 **继承** 自`BookStorePageBase`类(代替`PageModel`).`BookStorePageBase`类来自于启动模板,它提供了一些公开的可以被所有的页面使用的属性/方法.
* 此代码更改了Razor View Page Model的默认继承,因此它从`BookStorePageBase`类(而不是`PageModel`)继承.启动模板附带的`BookStorePageBase`类,提供所有页面使用的一些共享属性/方法.
* 确保`IndexModel`(Index.cshtml.cs)具有`Acme.BookStore.Pages.Books`命名空间,或者在`Index.cshtml`中更新它.
#### 将Books页面添加到主菜单
@ -337,17 +365,16 @@ context.Menu.AddItem(
#### 本地化菜单
本地化的文本在`Acme.BookStore.Domain`项目的`Localization/BookStore`文件夹中.
本地化文本位于`Acme.BookStore.Domain.Shared`项目的`Localization/BookStore`文件夹下:
![bookstore-localization-files](images/bookstore-localization-files.png)
![bookstore-localization-files](images/bookstore-localization-files-v2.png)
打开`en.json`文件,为`Menu:BookStore` 和 `Menu:Books`添加本地化文本:
打开`en.json`文件,将`Menu:BookStore`和`Menu:Books`键的本地化文本添加到文件末尾:
````json
{
"culture": "en",
"texts": {
//...
"Menu:BookStore": "Book Store",
"Menu:Books": "Books"
}
@ -355,9 +382,9 @@ context.Menu.AddItem(
````
* ABP的本地化功能建立在[ASP.NET Core's standard localization]((https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization))之上并增加了一些扩展.查看[本地化文档](../../Localization.md).
* 本地化中key的名字是随便定义的,你可以随意命名.我们喜欢为菜单添加`Menu`命名空间,以区别于其他的文本.如果文本没有在本地化文件中定义,就会 **返回** 本地的化的key(ASP.NET Core的标准做法).
* 本地化key是任意的. 你可以设置任何名称. 我们更喜欢为菜单项添加`Menu:`前缀以区别于其他文本. 如果未在本地化文件中定义文本,则它将**返回**到本地化的key(ASP.NET Core的标准行为).
运行程序就会看到菜单已经添加到了顶部:
运行该应用程序,看到新菜单项已添加到顶部栏:
![bookstore-menu-items](images/bookstore-menu-items.png)
@ -365,17 +392,16 @@ context.Menu.AddItem(
#### 书籍列表
我们会在页面上使用JQuery插件[Datatables.net](https://datatables.net/)来展示列表.Datatables可以完全通过AJAX工作,所以它很快而且有良好的用户体验.启动模板中已经配置好了Datatables插件,因此你可以在你的页面中直接使用,不需要引用样式和脚本文件.
我们将使用[Datatables.net](https://datatables.net/)JQuery插件来显示页面上的表格列表. 数据表可以完全通过AJAX工作,速度快,并提供良好的用户体验. Datatables插件在启动模板中配置,因此你可以直接在任何页面中使用它,而需要在页面中引用样式和脚本文件.
##### 修改Index.cshtml
##### Index.cshtml
将`Pages/Books/Index.cshtml`改成下面的样子:
````html
@page
@using Acme.BookStore.Pages.Books
@inherits Acme.BookStore.Pages.BookStorePageBase
@model IndexModel
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@model Acme.BookStore.Web.Pages.Books.IndexModel
@section scripts
{
<abp-script src="/Pages/Books/index.js" />
@ -408,7 +434,7 @@ context.Menu.AddItem(
在`Pages/Books/`文件夹中创建 `index.js`文件
![bookstore-index-js-file](images/bookstore-index-js-file.png)
![bookstore-index-js-file](images/bookstore-index-js-file-v2.png)
`index.js`的内容如下:
@ -430,7 +456,7 @@ $(function () {
* `abp.libs.datatables.createAjax`是帮助ABP的动态JavaScript API代理跟Datatable的格式相适应的辅助方法.
* `abp.libs.datatables.normalizeConfiguration`是另一个辅助方法.不是必须的, 但是它通过为缺少的选项提供常规值来简化数据表配置.
* `acme.bookStore.book.getList`是获取书籍列表的方法(上面已经介绍过了)
* 查看 [Datatable's 文档](https://datatables.net/manual/) 了解更多配置项.
* 查看 [Datatable文档](https://datatables.net/manual/) 了解更多配置项.
最终的页面如下:

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

@ -2,13 +2,13 @@
### 关于本教程
这是本教程所有章节中的第二章.下面是所有的章节:
这是ASP.NET Core MVC教程系列的第二章. 查看其它章节
* [Part I: 创建项目和书籍列表页面](Part-I.md)
* **Part II: 创建,编辑,删除书籍(本章)**
* [Part III: 集成测试](Part-III.md)
你可以从 [这里](https://github.com/volosoft/abp/tree/master/samples/BookStore) 下载本程序的**源码**.
你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**.
### 新增 Book 实体
@ -20,7 +20,7 @@
在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个 `CreateModal.cshtml` Razor页面:
![bookstore-add-create-dialog](images/bookstore-add-create-dialog.png)
![bookstore-add-create-dialog](images/bookstore-add-create-dialog-v2.png)
##### CreateModal.cshtml.cs
@ -30,7 +30,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Acme.BookStore.Pages.Books
namespace Acme.BookStore.Web.Pages.Books
{
public class CreateModalModel : BookStorePageModelBase
{
@ -63,9 +63,9 @@ namespace Acme.BookStore.Pages.Books
````html
@page
@inherits Acme.BookStore.Pages.BookStorePageBase
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Pages.Books.CreateModalModel
@model Acme.BookStore.Web.Pages.Books.CreateModalModel
@{
Layout = null;
}
@ -138,12 +138,12 @@ $('#NewBookButton').click(function (e) {
展开 `EditModal.cshtml`,打开 `EditModal.cshtml.cs` 文件( `EditModalModel` 类) 并替换成以下代码:
````C#
````csharp
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Acme.BookStore.Pages.Books
namespace Acme.BookStore.Web.Pages.Books
{
public class EditModalModel : BookStorePageModelBase
{
@ -180,38 +180,26 @@ namespace Acme.BookStore.Pages.Books
* 在 `OnGetAsync` 方法中,将 `BookAppService.GetAsync` 方法返回的 `BookDto` 映射成 `CreateUpdateBookDto` 并赋值给Book属性.
* `OnPostAsync` 方法直接使用 `BookAppService.UpdateAsync` 来更新实体.
#### CreateUpdateBookDto
#### BookDto到CreateUpdateBookDto对象映射
为了执行从 `BookDto` 到 `CreateUpdateBookDto` 的对象映射, 按如下所示修改 `CreateUpdateBookDto`类:
为了执行`BookDto`到`CreateUpdateBookDto`对象映射,请打开`Acme.BookStore.Web`项目中的`BookStoreWebAutoMapperProfile.cs`并更改它,如下所示:
````C#
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AutoMapper;
````csharp
using AutoMapper;
namespace Acme.BookStore
namespace Acme.BookStore.Web
{
[AutoMapTo(typeof(Book))]
[AutoMapFrom(typeof(BookDto))]
public class CreateUpdateBookDto
public class BookStoreWebAutoMapperProfile : Profile
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
public DateTime PublishDate { get; set; }
[Required]
public float Price { get; set; }
public BookStoreWebAutoMapperProfile()
{
CreateMap<BookDto, CreateUpdateBookDto>();
}
}
}
````
* 仅添加 `[AutoMapFrom(typeof(BookDto))]` 特性就可以创建上述映射关系.
* 刚刚添加了`CreateMap<BookDto, CreateUpdateBookDto>();`作为映射定义.
#### EditModal.cshtml
@ -219,8 +207,8 @@ namespace Acme.BookStore
````html
@page
@inherits Acme.BookStore.Pages.BookStorePageBase
@using Acme.BookStore.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePageBase
@using Acme.BookStore.Web.Pages.Books
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel
@{
@ -231,7 +219,7 @@ namespace Acme.BookStore
<abp-modal-header title="@L["Update"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Id" />
<abp-form-content/>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
@ -240,8 +228,8 @@ namespace Acme.BookStore
这个页面内容和 `CreateModal.cshtml` 非常相似,除了以下几点:
* 此页面包含了一个 `abp-input` 以保存所编辑book实体的 `Id` 属性值.
* 此页面指定的post地址是 `Books/EditModal` ,并用文本 *Update* 作为 modal 标题.
* 它包含`id`属性的`abp-input`, 用于存储编辑书的id(它是隐藏的Input)
* 此页面指定的post地址是`Books/EditModal`, 并用文本 *Update* 作为 modal 标题.
#### 为表格添加 "操作(Actions)" 下拉菜单
@ -420,6 +408,12 @@ $(function () {
});
````
打开`Acme.BookStore.Domain.Shared`项目中的`en.json`并添加以下行:
````json
"BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?"
````
运行程序并尝试删除一个book实体.
### 下一章

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

@ -2,23 +2,21 @@
### 关于本教程
这是本教程所有章节中的第三章.下面是所有的章节:
这是ASP.NET Core MVC教程系列的第三章. 查看其它章节
- [Part I: 创建项目和书籍列表页面](Part-I.md)
- [Part II: 创建,编辑,删除书籍](Part-II.md)
- **Part III: 集成测试(本章)**
你可以从 [这里](https://github.com/volosoft/abp/tree/master/samples/BookStore) 下载本程序的**源码**.
你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**.
### 解决方案中的测试项目
本解决方案中有两个测试项目:
解决方案中有多个测试项目:
![bookstore-test-projects-v2](images/bookstore-test-projects-v2.png)
* `Acme.BookStore.Application.Tests` 项目用于单元测试和集成测试.你可以在这个项目中为Application Service方法写测试代码.这个项目使用了 **EF Core SQLite in-memory** 数据库.
* `Acme.BookStore.Web.Tests` 项目用于包含Web层的完整集成测试.所以,你也可以在这里写关于UI页面的测试.
测试项目使用了以下库:
每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试:
* [xunit](https://xunit.github.io/) 作为主测试框架.
* [Shoudly](http://shouldly.readthedocs.io/en/latest/) 作为断言库.
@ -26,79 +24,39 @@
### 添加测试用数据
起始模板在 `Acme.BookStore.Application.Tests` 项目中包含了 `BookStoreTestDataBuilder` 类,用于创建一些测试用数据. 相关代码如下所示:
````C#
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Acme.BookStore
{
public class BookStoreTestDataBuilder : ITransientDependency
{
private readonly IIdentityDataSeeder _identityDataSeeder;
public BookStoreTestDataBuilder(IIdentityDataSeeder identityDataSeeder)
{
_identityDataSeeder = identityDataSeeder;
}
public void Build()
{
AsyncHelper.RunSync(BuildInternalAsync);
}
public async Task BuildInternalAsync()
{
await _identityDataSeeder.SeedAsync("1q2w3E*");
}
}
}
````
* 这里直接使用了identity模块实现的 `IIdentityDataSeeder` 接口,创建了一个admin角色和admin用户.你可以在测试代码中使用它们.
* 你可以在 `BuildInternalAsync` 方法中添加你自己的测试数据.
按下方所示修改 `BookStoreTestDataBuilder` 类:
启动模板包含`Acme.BookStore.TestBase`项目中的`BookStoreTestDataSeedContributor`类,它创建一些数据来运行测试.
更改`BookStoreTestDataSeedContributor`类如下所示:
````C#
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
using Volo.Abp.Guids;
namespace Acme.BookStore
{
public class BookStoreTestDataBuilder : ITransientDependency
public class BookStoreTestDataSeedContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IIdentityDataSeeder _identityDataSeeder;
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreTestDataBuilder(
IIdentityDataSeeder identityDataSeeder,
IRepository<Book, Guid> bookRepository)
public BookStoreTestDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
{
_identityDataSeeder = identityDataSeeder;
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public void Build()
public async Task SeedAsync(DataSeedContext context)
{
AsyncHelper.RunSync(BuildInternalAsync);
}
public async Task BuildInternalAsync()
{
await _identityDataSeeder.SeedAsync("1q2w3E*");
await _bookRepository.InsertAsync(
new Book
{
Id = Guid.NewGuid(),
Id = _guidGenerator.Create(),
Name = "Test book 1",
Type = BookType.Fantastic,
PublishDate = new DateTime(2015, 05, 24),
@ -109,7 +67,7 @@ namespace Acme.BookStore
await _bookRepository.InsertAsync(
new Book
{
Id = Guid.NewGuid(),
Id = _guidGenerator.Create(),
Name = "Test book 2",
Type = BookType.Science,
PublishDate = new DateTime(2014, 02, 11),
@ -121,7 +79,8 @@ namespace Acme.BookStore
}
````
* 通过构造函数注入 `IRepository<Book, Guid>`,在 `BuildInternalAsync` 方法中用它创建两个book实体.
* 注入`IRepository<Book,Guid>`并在`SeedAsync`中使用它来创建两个书实体作为测试数据.
* 使用`IGuidGenerator`服务创建GUID. 虽然`Guid.NewGuid()`非常适合测试,但`IGuidGenerator`在使用真实数据库时还有其他特别重要的功能(参见[Guid生成文档](../../Guid-Generation.md)了解更多信息).
### 测试 BookAppService
@ -211,6 +170,8 @@ public async Task Should_Not_Create_A_Book_Without_Name()
* 由于 `Name` 是空值, ABP 抛出一个 `AbpValidationException` 异常.
### 测试 Web 页面
打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行**所有测试:
![bookstore-appservice-tests](images/bookstore-appservice-tests.png)
TODO
恭喜, 绿色图标表示测试已成功通过!

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

3
docs/zh-Hans/Value-Types.md

@ -0,0 +1,3 @@
## Value Types
TODO

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

@ -41,6 +41,10 @@
}
]
},
{
"text": "CLI",
"path": "CLI.md"
},
{
"text": "基础知识",
"items": [

BIN
docs/zh-Hans/images/bookstore-user-management-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/zh-Hans/images/bookstore-user-management.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/zh-Hans/images/bookstore-visual-studio-solution.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/zh-Hans/images/db-migrator-app.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

70
framework/Volo.Abp.sln

@ -1,7 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.156
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}"
EndProject
@ -219,11 +218,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp", "src\Volo.Abp\Vo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Authentication.JwtBearer", "src\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj", "{46C6336C-A1D8-4858-98CE-6F4C698C5A77}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Cli", "src\Volo.Abp.Cli\Volo.Abp.Cli.csproj", "{69168816-4394-4DDA-BB6B-C21983D37F0B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli", "src\Volo.Abp.Cli\Volo.Abp.Cli.csproj", "{69168816-4394-4DDA-BB6B-C21983D37F0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Sqlite", "src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj", "{58CF8957-5045-4F81-884D-72DF48F721CC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli.Core", "src\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj", "{3DA9923E-048E-4FE7-9748-3A0194F5D196}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Specifications", "src\Volo.Abp.Specifications\Volo.Abp.Specifications.csproj", "{2C621EED-563C-4F81-A75E-50879E173544}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Specifications.Tests", "test\Volo.Abp.Specifications.Tests\Volo.Abp.Specifications.Tests.csproj", "{D078553A-C70C-4F56-B3E2-9C5BA6384C72}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli.Core.Tests", "test\Volo.Abp.Cli.Core.Tests\Volo.Abp.Cli.Core.Tests.csproj", "{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Widgets", "src\Volo.Abp.AspNetCore.Mvc.UI.Widgets\Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj", "{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Dashboards", "src\Volo.Abp.AspNetCore.Mvc.UI.Dashboards\Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj", "{054D766D-5992-460E-A4D8-936D80BE2C1A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -655,6 +668,10 @@ Global
{46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Release|Any CPU.Build.0 = Release|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.Build.0 = Release|Any CPU
{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -663,10 +680,34 @@ Global
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.Build.0 = Release|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.Build.0 = Release|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Release|Any CPU.Build.0 = Release|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Release|Any CPU.Build.0 = Release|Any CPU
{2C621EED-563C-4F81-A75E-50879E173544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C621EED-563C-4F81-A75E-50879E173544}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C621EED-563C-4F81-A75E-50879E173544}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C621EED-563C-4F81-A75E-50879E173544}.Release|Any CPU.Build.0 = Release|Any CPU
{D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Release|Any CPU.Build.0 = Release|Any CPU
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Release|Any CPU.Build.0 = Release|Any CPU
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Release|Any CPU.Build.0 = Release|Any CPU
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -778,9 +819,16 @@ Global
{575BEFA1-19C2-49B1-8D31-B5D4472328DE} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{6C161F55-54B6-42A5-B177-3B0ED50323C1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{46C6336C-A1D8-4858-98CE-6F4C698C5A77} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{69168816-4394-4DDA-BB6B-C21983D37F0B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{43D5FE61-ECBF-4B16-AD95-0043E18EB93A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{69168816-4394-4DDA-BB6B-C21983D37F0B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{58CF8957-5045-4F81-884D-72DF48F721CC} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{3DA9923E-048E-4FE7-9748-3A0194F5D196} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{2C621EED-563C-4F81-A75E-50879E173544} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{D078553A-C70C-4F56-B3E2-9C5BA6384C72} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{054D766D-5992-460E-A4D8-936D80BE2C1A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs

@ -205,6 +205,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual List<ModelExpression> ExploreModelsRecursively(List<ModelExpression> list, ModelExplorer model)
{
if (model.GetAttribute<DynamicFormIgnore>() != null)
{
return list;
}
if (IsCsharpClassOrPrimitive(model.ModelType) || IsListOfCsharpClassOrPrimitive(model.ModelType))
{
list.Add(ModelExplorerToModelExpressionConverter(model));

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -35,10 +35,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
TagHelper.AspFor.Name,
SurroundInnerHtmlAndGet(context, output, innerHtml, isCheckbox),
order,
out var surpress
out var suppress
);
if (surpress)
if (suppress)
{
output.SuppressOutput();
}
@ -389,10 +389,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
return idAttr != null ? "for=\"" + idAttr.Value + "\"" : "";
}
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress)
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress)
{
var list = context.GetValue<List<FormGroupItem>>(FormGroupContents) ?? new List<FormGroupItem>();
surpress = list == null;
suppress = list == null;
if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\"")))
{

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs

@ -29,9 +29,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
var html = GetHtml(context, output, selectItems);
AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, html, order, out var surpress);
AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, html, order, out var suppress);
if (surpress)
if (suppress)
{
output.SuppressOutput();
}
@ -157,10 +157,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
return TagHelper.AspFor.ModelExplorer.Model?.ToString();
}
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress)
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress)
{
var list = context.GetValue<List<FormGroupItem>>(FormGroupContents) ?? new List<FormGroupItem>();
surpress = list == null;
suppress = list == null;
if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\"")))
{

26
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -33,9 +33,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
var order = TagHelper.AspFor.ModelExplorer.GetDisplayOrder();
AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, SurroundInnerHtmlAndGet(context, output, innerHtml), order, out var surpress);
AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, SurroundInnerHtmlAndGet(context, output, innerHtml), order, out var suppress);
if (surpress)
if (suppress)
{
output.SuppressOutput();
}
@ -187,8 +187,22 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{
var localizer = _tagHelperLocalizer.GetLocalizer(explorer);
var selectItems = explorer.Metadata.IsEnum ? explorer.ModelType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static)
.Select((t, i) => new SelectListItem { Value = i.ToString(), Text = GetLocalizedPropertyName(localizer, explorer.ModelType, t.Name) }).ToList() : null;
var selectItems = new List<SelectListItem>();
var isNullableType = Nullable.GetUnderlyingType(explorer.ModelType) != null;
var enumType = explorer.ModelType;
if (isNullableType)
{
enumType = Nullable.GetUnderlyingType(explorer.ModelType);
selectItems.Add(new SelectListItem());
}
selectItems.AddRange(enumType.GetEnumNames()
.Select(enumName => new SelectListItem
{
Value = Convert.ToUInt64(Enum.Parse(enumType, enumName)).ToString(),
Text = GetLocalizedPropertyName(localizer, enumType, enumName)
}));
return selectItems;
}
@ -304,10 +318,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
return idAttr != null ? "for=\"" + idAttr.Value + "\"" : "";
}
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress)
protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress)
{
var list = context.GetValue<List<FormGroupItem>>(FormGroupContents) ?? new List<FormGroupItem>();
surpress = list == null;
suppress = list == null;
if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\"")))
{

9
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DynamicFormIgnore.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
{
[AttributeUsage(AttributeTargets.Property)]
public class DynamicFormIgnore : Attribute
{
}
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs

@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling

27
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:57333/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.Abp.AspNetCore.Mvc.UI.Dashboards": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:57343/"
}
}
}

35
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.AspNetCore.Mvc.UI.Dashboards</AssemblyName>
<PackageId>Volo.Abp.AspNetCore.Mvc.UI.Dashboards</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\Components\**\*.cshtml" />
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\Components\**\*.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\_ViewImports.cshtml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\Dashboards\_ViewImports.cshtml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Widgets\Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bundling\Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj" />
</ItemGroup>
</Project>

20
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs

@ -0,0 +1,20 @@
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
{
[DependsOn(
typeof(AbpAspNetCoreMvcUiWidgetsModule)
)]
public class AbpAspNetCoreMvcUiDashboardsModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiDashboardsModule>();
});
}
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs

@ -0,0 +1,8 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards
{
public class AbpBasicDashboardScriptContributor : BundleContributor
{
}
}

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

Loading…
Cancel
Save