From 984089d770dd8e1c82674ce3df1c2c3e0428fc8c Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 10 Oct 2018 13:33:41 +0800 Subject: [PATCH] Chinese document structure & translation of index.md, docs-nav.json. --- docs/zh-Hans/Application-Services.md | 3 + .../AspNetCore/Bundling-Minification.md | 323 +++++++++++++ .../Client-Side-Package-Management.md | 115 +++++ docs/zh-Hans/AspNetCore/Tag-Helpers.md | 3 + docs/zh-Hans/AspNetCore/Theming.md | 3 + docs/zh-Hans/AutoMapper-Integration.md | 3 + docs/zh-Hans/Background-Jobs.md | 23 + .../Best-Practices/Application-Services.md | 209 +++++++++ .../Best-Practices/Data-Transfer-Objects.md | 7 + .../zh-Hans/Best-Practices/Domain-Services.md | 3 + docs/zh-Hans/Best-Practices/Entities.md | 153 +++++++ .../Entity-Framework-Core-Integration.md | 209 +++++++++ docs/zh-Hans/Best-Practices/Index.md | 27 ++ .../Best-Practices/Module-Architecture.md | 89 ++++ .../Best-Practices/MongoDB-Integration.md | 201 +++++++++ docs/zh-Hans/Best-Practices/Repositories.md | 92 ++++ .../2018-09-24-Announcement/Post.md | 174 +++++++ .../2018-09-24-Announcement/dynamic-forms.png | Bin 0 -> 8842 bytes docs/zh-Hans/Contribution/Index.md | 52 +++ .../Contribution/Localization-Text-Files.md | 20 + docs/zh-Hans/Data-Transfer-Objects.md | 3 + docs/zh-Hans/Dependency-Injection.md | 271 +++++++++++ docs/zh-Hans/Entities.md | 176 ++++++++ docs/zh-Hans/Entity-Framework-Core.md | 107 +++++ docs/zh-Hans/Exception-Handling.md | 302 +++++++++++++ .../Getting-Started-AspNetCore-Application.md | 157 +++++++ ...Getting-Started-AspNetCore-MVC-Template.md | 71 +++ .../Getting-Started-Console-Application.md | 124 +++++ docs/zh-Hans/Index.md | 64 +++ docs/zh-Hans/Localization.md | 191 ++++++++ docs/zh-Hans/Module-Development-Basics.md | 128 ++++++ docs/zh-Hans/MongoDB.md | 99 ++++ docs/zh-Hans/Multi-Tenancy.md | 389 ++++++++++++++++ docs/zh-Hans/Object-To-Object-Mapping.md | 3 + docs/zh-Hans/Repositories.md | 117 +++++ docs/zh-Hans/Tag-Helpers.md | 3 + .../Tutorials/AspNetCore-Mvc/Part-I.md | 426 ++++++++++++++++++ .../Tutorials/AspNetCore-Mvc/Part-II.md | 421 +++++++++++++++++ .../Tutorials/AspNetCore-Mvc/Part-III.md | 217 +++++++++ .../images/bookstore-add-create-dialog.png | Bin 0 -> 8437 bytes .../images/bookstore-add-edit-dialog.png | Bin 0 -> 4006 bytes .../images/bookstore-add-index-page.png | Bin 0 -> 8699 bytes .../images/bookstore-book-list.png | Bin 0 -> 33481 bytes .../images/bookstore-books-table-actions.png | Bin 0 -> 9339 bytes .../images/bookstore-books-table.png | Bin 0 -> 12903 bytes .../images/bookstore-create-dialog.png | Bin 0 -> 35217 bytes .../images/bookstore-create-template.png | Bin 0 -> 32422 bytes .../images/bookstore-homepage.png | Bin 0 -> 11607 bytes .../images/bookstore-index-js-file.png | Bin 0 -> 3592 bytes .../images/bookstore-localization-files.png | Bin 0 -> 5416 bytes .../images/bookstore-menu-items.png | Bin 0 -> 10649 bytes .../images/bookstore-new-book-button.png | Bin 0 -> 34614 bytes .../bookstore-pmc-add-book-migration.png | Bin 0 -> 8135 bytes .../images/bookstore-swagger.png | Bin 0 -> 35254 bytes ...ookstore-test-js-proxy-getlist-network.png | Bin 0 -> 22325 bytes .../bookstore-test-js-proxy-getlist.png | Bin 0 -> 21562 bytes .../images/bookstore-test-projects.png | Bin 0 -> 7399 bytes .../images/bookstore-user-management.png | Bin 0 -> 19157 bytes .../bookstore-visual-studio-solution.png | Bin 0 -> 7369 bytes docs/zh-Hans/Virtual-File-System.md | 165 +++++++ docs/zh-Hans/docs-nav.json | 235 ++++++++++ .../images/bookstore-create-template.png | Bin 0 -> 32422 bytes docs/zh-Hans/images/bookstore-homepage.png | Bin 0 -> 11607 bytes .../images/bookstore-user-management.png | Bin 0 -> 19157 bytes .../bookstore-visual-studio-solution.png | Bin 0 -> 7369 bytes .../build-action-embedded-resource-sample.png | Bin 0 -> 6844 bytes .../create-new-aspnet-core-application.png | Bin 0 -> 33930 bytes ...reate-new-net-core-console-application.png | Bin 0 -> 36231 bytes .../disable-package-restore-visual-studio.png | Bin 0 -> 24425 bytes .../localization-resource-json-files.png | Bin 0 -> 2798 bytes .../images/module-layers-and-packages.jpg | Bin 0 -> 26892 bytes docs/zh-Hans/images/pcm-update-database.png | Bin 0 -> 6450 bytes .../images/select-empty-web-application.png | Bin 0 -> 26780 bytes .../zh-Hans/images/set-as-startup-project.png | Bin 0 -> 3923 bytes 74 files changed, 5378 insertions(+) create mode 100644 docs/zh-Hans/Application-Services.md create mode 100644 docs/zh-Hans/AspNetCore/Bundling-Minification.md create mode 100644 docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md create mode 100644 docs/zh-Hans/AspNetCore/Tag-Helpers.md create mode 100644 docs/zh-Hans/AspNetCore/Theming.md create mode 100644 docs/zh-Hans/AutoMapper-Integration.md create mode 100644 docs/zh-Hans/Background-Jobs.md create mode 100644 docs/zh-Hans/Best-Practices/Application-Services.md create mode 100644 docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md create mode 100644 docs/zh-Hans/Best-Practices/Domain-Services.md create mode 100644 docs/zh-Hans/Best-Practices/Entities.md create mode 100644 docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md create mode 100644 docs/zh-Hans/Best-Practices/Index.md create mode 100644 docs/zh-Hans/Best-Practices/Module-Architecture.md create mode 100644 docs/zh-Hans/Best-Practices/MongoDB-Integration.md create mode 100644 docs/zh-Hans/Best-Practices/Repositories.md create mode 100644 docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/Post.md create mode 100644 docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/dynamic-forms.png create mode 100644 docs/zh-Hans/Contribution/Index.md create mode 100644 docs/zh-Hans/Contribution/Localization-Text-Files.md create mode 100644 docs/zh-Hans/Data-Transfer-Objects.md create mode 100644 docs/zh-Hans/Dependency-Injection.md create mode 100644 docs/zh-Hans/Entities.md create mode 100644 docs/zh-Hans/Entity-Framework-Core.md create mode 100644 docs/zh-Hans/Exception-Handling.md create mode 100644 docs/zh-Hans/Getting-Started-AspNetCore-Application.md create mode 100644 docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md create mode 100644 docs/zh-Hans/Getting-Started-Console-Application.md create mode 100644 docs/zh-Hans/Index.md create mode 100644 docs/zh-Hans/Localization.md create mode 100644 docs/zh-Hans/Module-Development-Basics.md create mode 100644 docs/zh-Hans/MongoDB.md create mode 100644 docs/zh-Hans/Multi-Tenancy.md create mode 100644 docs/zh-Hans/Object-To-Object-Mapping.md create mode 100644 docs/zh-Hans/Repositories.md create mode 100644 docs/zh-Hans/Tag-Helpers.md create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-book-list.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-dialog.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-homepage.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-new-book-button.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-swagger.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist-network.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-js-proxy-getlist.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-user-management.png create mode 100644 docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png create mode 100644 docs/zh-Hans/Virtual-File-System.md create mode 100644 docs/zh-Hans/docs-nav.json create mode 100644 docs/zh-Hans/images/bookstore-create-template.png create mode 100644 docs/zh-Hans/images/bookstore-homepage.png create mode 100644 docs/zh-Hans/images/bookstore-user-management.png create mode 100644 docs/zh-Hans/images/bookstore-visual-studio-solution.png create mode 100644 docs/zh-Hans/images/build-action-embedded-resource-sample.png create mode 100644 docs/zh-Hans/images/create-new-aspnet-core-application.png create mode 100644 docs/zh-Hans/images/create-new-net-core-console-application.png create mode 100644 docs/zh-Hans/images/disable-package-restore-visual-studio.png create mode 100644 docs/zh-Hans/images/localization-resource-json-files.png create mode 100644 docs/zh-Hans/images/module-layers-and-packages.jpg create mode 100644 docs/zh-Hans/images/pcm-update-database.png create mode 100644 docs/zh-Hans/images/select-empty-web-application.png create mode 100644 docs/zh-Hans/images/set-as-startup-project.png diff --git a/docs/zh-Hans/Application-Services.md b/docs/zh-Hans/Application-Services.md new file mode 100644 index 0000000000..3dc24c9d9a --- /dev/null +++ b/docs/zh-Hans/Application-Services.md @@ -0,0 +1,3 @@ +## Application Services + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/AspNetCore/Bundling-Minification.md b/docs/zh-Hans/AspNetCore/Bundling-Minification.md new file mode 100644 index 0000000000..af92d91243 --- /dev/null +++ b/docs/zh-Hans/AspNetCore/Bundling-Minification.md @@ -0,0 +1,323 @@ + +## ASP.NET Core MVC Bundling & Minification + +There are many ways of bundling & minification of client side resources (JavaScript and CSS files). Most common ways are: + +* Using the [Bundler & Minifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier) Visual Studio extension or the [NuGet package](https://www.nuget.org/packages/BuildBundlerMinifier/). +* Using [Gulp](https://gulpjs.com/)/[Grunt](https://gruntjs.com/) task managers and their plugins. + +ABP offers a simple, dynamic, powerful, modular and built-in way. + +### Volo.Abp.AspNetCore.Mvc.UI.Bundling Package + +> This package is already installed by default with the startup templates. So, most of the time, you don't need to install it manually. + +Install the `Volo.Abp.AspNetCore.Mvc.UI.Bundling` nuget package to your project: + +```` +install-package Volo.Abp.AspNetCore.Mvc.UI.Bundling +```` + +Then you can add the `AbpAspNetCoreMvcUiBundlingModule` dependency to your module: + +````C# +using Volo.Abp.Modularity; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))] + public class MyWebModule : AbpModule + { + //... + } +} +```` + +### Razor Bundling Tag Helpers + +The simplest way of creating a bundle is to use `abp-script-bundle` or `abp-style-bundle` tag helpers. Example: + +````html + + + + + + +```` + +This bundle defines a style bundle with a **unique name**: `MyGlobalBundle`. It's very easy to understand how to use it. Let's see how it *works*: + +* ABP creates the bundle as **lazy** from the provided files when it's **first requested**. For the subsequent calls, it's returned from the **cache**. That means if you conditionally add the files to the bundle, it's executed only once and any changes of the condition will not effect the bundle for the next requests. +* ABP adds bundle files **individually** to the page for the `development` environment. It automatically bundles & minifies for other environments (`staging`, `production`...). +* The bundle files may be **physical** files or [**virtual/embedded** files](../Virtual-File-System.md). +* ABP automatically adds **version query string** to the bundle file URL to prevent browsers from caching when the bundle is being updated. (like ?_v=67872834243042 - generated from last change date of the related files). The versioning works even if the bundle files are individually added to the page (on the development environment). + +#### Importing The Bundling Tag Helpers + +> This is already imported by default with the startup templates. So, most of the time, you don't need to add it manually. + +In order to use bundle tag helpers, you need to add it into your `_ViewImports.cshtml` file or into your page: + +```` +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling +```` + +#### Unnamed Bundles + +The `name` is **optional** for the razor bundle tag helpers. If you don't define a name, it's automatically **calculated** based on the used bundle file names (they are **concatenated** and **hashed**). Example: + +````html + + + + + @if (ViewBag.IncludeCustomStyles != false) + { + + } + +```` + +This will potentially create **two different bundles** (one incudes the `my-global-style.css` and other does not). + +Advantages of **unnamed** bundles: + +* Can **conditionally add items** to the bundle. But this may lead to multiple variations of the bundle based on the conditions. + +Advantages of **named** bundles: + +* Other **modules can contribute** to the bundle by its name (see the sections below). + +#### Single File + +If you need to just add a single file to the page, you can use the `abp-script` or `abp-style` tag without a wrapping in the `abp-script-bundle` or `abp-style-bundle` tag. Example: + +````xml + +```` + +The bundle name will be *scripts.my-scripts* for the example above ("/" is replaced by "."). All bundling features are work as expected for single file bundles too. + +### Bundling Options + +If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../Module-Development-Basics.md) class. + +#### Creating A New Bundle + +Example usage: + +````C# +[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))] +public class MyWebModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + options + .ScriptBundles + .Add("MyGlobalBundle", bundle => { + bundle.AddFiles( + "/libs/jquery/jquery.js", + "/libs/bootstrap/js/bootstrap.js", + "/libs/toastr/toastr.min.js", + "/scripts/my-global-scripts.js" + ); + }); + }); + } +} +```` + +> You can use the same name (*MyGlobalBundle* here) for a script & style bundle since they are added to different collections (`ScriptBundles` and `StyleBundles`). + +After defining such a bundle, it can be included into a page using the same tag helpers defined above. Example: + +````html + +```` + +This time, no file defined in the tag helper definition because the bundle files are defined by the code. + +#### Configuring An Existing Bundle + +ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a dependant module. +Example: + +````C# +[DependsOn(typeof(MyWebModule))] +public class MyWebExtensionModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + options + .ScriptBundles + .Configure("MyGlobalBundle", bundle => { + bundle.AddFiles( + "/scripts/my-extension-script.js" + ); + }); + }); + } +} +```` + +> It's not possible to configure unnamed bundle tag helpers by code, because their name are not known at the development time. It's suggested to always use a name for a bundle tag helper. + +### Bundle Contributors + +Adding files to an existing bundle seems useful. What if you need to **replace** a file in the bundle or you want to **conditionally** add files? Defining a bundle contributor provides extra power for such cases. + +An example bundle contributor that replaces bootstrap.css with a customized version: + +````C# +public class MyExtensionGlobalStyleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.ReplaceOne( + "/libs/bootstrap/css/bootstrap.css", + "/styles/extensions/bootstrap-customized.css" + ); + } +} +```` + +Then you can use this contributor as below: + +````C# +services.Configure(options => +{ + options + .ScriptBundles + .Configure("MyGlobalBundle", bundle => { + bundle.AddContributors(typeof(MyExtensionStyleBundleContributor)); + }); +}); +```` + +Contributors can also be used in the bundle tag helpers. +Example: + +````xml + + + + + +```` + +`abp-style` and `abp-script` tags can get `type` attributes (instead of `src` attributes) as shown in this sample. When you add a bundle contributor, its dependencies are also automatically added to the bundle. + +#### Contributor Dependencies + +A bundle contributor can have one or more dependencies to other contributors. +Example: + +````C# +[DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency +public class MyExtensionStyleBundleContributor : BundleContributor +{ + //... +} +```` + +When a bundle contributor is added, its dependencies are **automatically and recursively** added. Dependencies added by the **dependency order** by preventing **duplicates**. Duplicates are prevented even if they are in separated bundles. ABP organizes all bundles in a page and eliminates duplications. + +Creating contributors and defining dependencies is a way of organizing bundle creation across different modules. + +#### Accessing to the IServiceProvider + +While it is rarely needed, `BundleConfigurationContext` has a `ServiceProvider` property that you can resolve service dependencies inside the `ConfigureBundle` method. + +#### Standard Package Contributors + +Adding a specific NPM package resource (js, css files) into a bundle is pretty straight forward for that package. For example you always add the `bootstrap.css` file for the bootstrap NPM package. + +There are built-in contributors for all [standard NPM packages](Client-Side-Package-Management.md). For example, if your contributor depends on the bootstrap, you can just declare it, instead of adding the bootstrap.css yourself. + +````C# +[DependsOn(typeof(BootstrapStyleContributor))] //Define the bootstrap style dependency +public class MyExtensionStyleBundleContributor : BundleContributor +{ + //... +} +```` + +Using the built-in contributors for standard packages; + +* Prevents you typing **invalid the resource paths**. +* Prevents changing your contributor if the resource **path changes** (the dependant contributor will handle it). +* Prevents multiple modules adding the **duplicate the files**. +* Manages **dependencies recursively** (adds dependencies of dependencies, if necessary). + +##### Volo.Abp.AspNetCore.Mvc.UI.Packages Package + +> This package is already installed by default in the startup templates. So, most of the time, you don't need to install it manually. + +Standard package contributors are defined in the `Volo.Abp.AspNetCore.Mvc.UI.Packages` NuGet package. +To install it to your project: + +```` +install-package Volo.Abp.AspNetCore.Mvc.UI.Packages +```` + +Then add the `AbpAspNetCoreMvcUiPackagesModule` module dependency to your own module; + +````C# +using Volo.Abp.Modularity; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpAspNetCoreMvcUiPackagesModule))] + public class MyWebModule : AbpModule + { + //... + } +} +```` + +#### Bundle Inheritance + +In some specific cases, it may be needed to create a **new** bundle **inherited** from other bundle(s). Inheriting from a bundle (recursively) inherits all files/contributors of that bundle. Then the derived bundle can add or modify files/contributors **without modifying** the original bundle. +Example: + +````c# +services.Configure(options => +{ + options + .StyleBundles + .Add("MyTheme.MyGlobalBundle", bundle => { + bundle + .AddBaseBundles("MyGlobalBundle") //Can add multiple + .AddFiles( + "/styles/mytheme-global-styles.css" + ); + }); +}); +```` + +### Themes + +Themes uses the standard package contributors to add library resources to page layouts. Themes may also define some standard/global bundles, so any module can contribute to these standard/global bundles. See the [theming documentation](Theming.md) for more. + +### Best Practices & Suggestions + +It's suggested to define multiple bundles for an application, each one is used for different purposes. + +* **Global bundle**: Global style/script bundles are included to every page in the application. Themes already defines global style & script bundles. Your module can contribute to them. +* **Layout bundles**: This is a specific bundle to an individual layout. Only contains resources shared among all the pages use the layout. Use the bundling tag helpers to create the bundle as a good practice. +* **Module bundles**: For shared resources among the pages of an individual module. +* **Page bundles**: Specific bundles created for each page. Use the bundling tag helpers to create the bundle as a best practice. + +Establish a balance between performance, network bandwidth usage and count of many bundles. + +### See Also + +* [Client Side Package Management](Client-Side-Package-Management.md) +* [Theming](Theming.md) diff --git a/docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md b/docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md new file mode 100644 index 0000000000..3c1785daa5 --- /dev/null +++ b/docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md @@ -0,0 +1,115 @@ + +## ASP.NET Core MVC Client Side Package Management + +ABP framework can work with any type of client side package management systems. You can even decide to use no package management system and manage your dependencies manually. + +However, ABP framework works best with **NPM/Yarn**. By default, built-in modules are configured to work with NPM/Yarn. + +Finally, we suggest the [**Yarn**](https://yarnpkg.com/) over the NPM since it's faster, stable and also compatible with the NPM. + +### @ABP NPM Packages + +ABP is a modular platform. Every developer can create modules and the modules should work together in a **compatible** and **stable** state. + +One challenge is the **versions of the dependant NPM packages**. What if two different modules use the same JavaScript library but its different (and potentially incompatible) versions. + +To solve the versioning problem, we created a **standard set of packages** those depends on some common third-party libraries. Some example packages are [@abp/jquery](https://www.npmjs.com/package/@abp/jquery), [@abp/bootstrap](https://www.npmjs.com/package/@abp/bootstrap) and [@abp/font-awesome](https://www.npmjs.com/package/@abp/font-awesome). You can see the **list of packages** from the [Github repository](https://github.com/volosoft/abp/tree/master/npm/packs). + +The benefit of a **standard package** is: + +* It depends on a **standard version** of a package. Depending on this package is **safe** because all modules depend on the same version. +* It contains the gulp task to copy library resources (js, css, img... files) from the **node_modules** folder to **wwwroot/libs** folder. See the *Mapping The Library Resources* section for more. + +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" + } + } + + +It's suggested to depend on a standard package instead of directly depending on a third-party package. + +#### Package Installation + +After depending on a NPM package, all you should do is to run the **yarn** command from the command line to install all the packages and their dependencies: + +```` +yarn +```` + +Alternatively, you can use `npm install` but [Yarn](https://yarnpkg.com/) is suggested as mentioned before. + +#### Package Contribution + +If you need a third-party NPM package that is not in the standard set of packages, you can create a Pull Request on the Github [repository](https://github.com/volosoft/abp). A pull request that follows these rules is accepted: + +* Package name should be named as `@abp/package-name` for a `package-name` on NPM (example: `@abp/bootstrap` for the `bootstrap` package). +* It should be the **latest stable** version of the package. +* It should only depend a **single** third-party package. It can depend on multiple `@abp/*` packages. +* The package should include a `abp.resourcemapping.js` file formatted as defined in the *Mapping The Library Resources* section. This file should only map resources for the depended package. +* You also need to create [bundle contributor(s)](Bundling-Minification.md) for the package you have created. + +See current standard packages for examples. + +### Mapping The Library Resources + +Using NPM packages and NPM/Yarn tool is the de facto standard for client side libraries. NPM/Yarn tool creates a **node_modules** folder in the root folder of your web project. + +Next challenge is copying needed resources (js, css, img... files) from the `node_modules` into a folder inside the **wwwroot** folder to make it accessible to the clients/browsers. + +ABP defines a [Gulp](https://gulpjs.com/) based task to **copy resources** from **node_modules** to **wwwroot/libs** folder. Each **standard package** (see the *@ABP NPM Packages* section) defines the mapping for its own files. So, most of the time, you only configure dependencies. + +The **startup templates** are already configured to work all these out of the box. This section will explain the configuration options. + +#### Resource Mapping Definition File + +A module should define a JavaScript file named `abp.resourcemapping.js` which is formatted as in the example below: + +````js +module.exports = { + aliases: { + "@node_modules": "./node_modules", + "@libs": "./wwwroot/libs" + }, + clean: [ + "@libs" + ], + mappings: { + + } +} +```` + +* **aliases** section defines standard aliases (placeholders) that can be used in the mapping paths. **@node_modules** and **@libs** are required (by the standard packages), you can define your own aliases to reduce duplication. +* **clean** section is a list of folders to clean before copying the files. +* **mappings** section is a list of mappings of files/folders to copy. This example does not copy any resource itself, but depends on a standard package. + +An example mapping configuration is shown below: + +````js +mappings: { + "@node_modules/bootstrap/dist/css/bootstrap.css": "@libs/bootstrap/css/", + "@node_modules/bootstrap/dist/js/bootstrap.bundle.js": "@libs/bootstrap/js/" +} +```` + +#### Using The Gulp + +Once you properly configure the `abp.resourcemapping.js` file, you can run the gulp command from the command line: + +```` +gulp +```` + +When you run the `gulp`, all packages will copy their own resources into the **wwwroot/libs** folder. Running `yarn & gulp` is only necessary if you make a change in your dependencies in the **package.json** file. + +> When you run the Gulp command, dependencies of the application are resolved using the package.json file. The Gulp task automatically discovers and maps all resources from all dependencies (recursively). + +#### See Also + +* [Bundling & Minification](Bundling-Minification.md) +* [Theming](Theming.md) diff --git a/docs/zh-Hans/AspNetCore/Tag-Helpers.md b/docs/zh-Hans/AspNetCore/Tag-Helpers.md new file mode 100644 index 0000000000..4949a3ec63 --- /dev/null +++ b/docs/zh-Hans/AspNetCore/Tag-Helpers.md @@ -0,0 +1,3 @@ +# Tag Helpers + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/AspNetCore/Theming.md b/docs/zh-Hans/AspNetCore/Theming.md new file mode 100644 index 0000000000..470ef1a458 --- /dev/null +++ b/docs/zh-Hans/AspNetCore/Theming.md @@ -0,0 +1,3 @@ +# Theming + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/AutoMapper-Integration.md b/docs/zh-Hans/AutoMapper-Integration.md new file mode 100644 index 0000000000..d197861f25 --- /dev/null +++ b/docs/zh-Hans/AutoMapper-Integration.md @@ -0,0 +1,3 @@ +## AutoMapper Integration + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Background-Jobs.md b/docs/zh-Hans/Background-Jobs.md new file mode 100644 index 0000000000..12e0dbbeea --- /dev/null +++ b/docs/zh-Hans/Background-Jobs.md @@ -0,0 +1,23 @@ +## Background Jobs + +### Introduction + +Background jobs are used to queue some tasks to be executed in the background. You may need background jobs for several reasons. Here are some examples: + +- To perform **long-running tasks** without having the users wait. For example, a user presses a 'report' button to start a long-running reporting job. You add this job to the **queue** and send the report's result to your user via email when it's completed. +- To create **re-trying** and **persistent tasks** to **guarantee** that a code will be **successfully executed**. For example, you can send emails in a background job to overcome **temporary failures** and **guarantee** that it eventually will be sent. That way users do not wait while sending emails. + +Background jobs are **persistent** that means they will be **re-tried** and **executed** later even if your application crashes. + +ABP provides an **abstraction** module and **several implementations** for background jobs. It has a built-in/default implementation as well as Hangfire and RabbitMQ integrations. + +### Abstraction Module + +TODO + +### Creating a Background Job + +A background job is a class that implements the `IBackgroundJob` interface or derives from the `BackgroundJob` class. `TArgs` is a simple plain C# class to store the job data. + +An example background job to send emails in background: + diff --git a/docs/zh-Hans/Best-Practices/Application-Services.md b/docs/zh-Hans/Best-Practices/Application-Services.md new file mode 100644 index 0000000000..c5d1b51393 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Application-Services.md @@ -0,0 +1,209 @@ +## Application Services Best Practices & Conventions + +* **Do** create an application service for each **aggregate root**. + +### Application Service Interface + +* **Do** define an `interface` for each application service in the **application contracts** package. +* **Do** inherit from the `IApplicationService` interface. +* **Do** use the `AppService` postfix for the interface name (ex: `IProductAppService`). +* **Do** create DTOs (Data Transfer Objects) for inputs and outputs of the service. +* **Do not** get/return entities for the service methods. +* **Do** define DTOs based on the [DTO best practices](Data-Transfer-Objects.md). + +#### Outputs + +* **Avoid** to define too many output DTOs for same or related entities. Instead, define a **basic** and a **detailed** DTO for an entity. + +##### Basic DTO + +**Do** define a **basic** DTO for an entity. + +- Include all the **primitive properties** directly on the entity. + - Exception: Can **exclude** properties for **security** reasons (like User.Password). +- Include all the **sub collections** of the entity where every item in the collection is a simple **relation DTO**. + +Example: + +```c# +public class IssueDto : FullAuditedEntityDto +{ + public string Title { get; set; } + public string Text { get; set; } + public Guid? MilestoneId { get; set; } + public Collection Labels { get; set; } +} + +public class IssueLabelDto +{ + public Guid IssueId { get; set; } + public Guid LabelId { get; set; } +} +``` + +##### Detailed DTO + +**Do** define a **detailed** DTO for an entity if it has reference(s) to other aggregate roots. + +* Include all the **primitive properties** directly on the entity. + - Exception-1: Can **exclude** properties for **security** reasons (like `User.Password`). + - Exception-2: **Do** exclude reference properties (like `MilestoneId` in the example above). Will already add details for the reference properties. +* Include a **basic DTO** property for every reference property. +* Include all the **sub collections** of the entity where every item in the collection is the **basic DTO** of the related entity. + +Example: + +````C# +public class IssueWithDetailsDto : FullAuditedEntityDto +{ + public string Title { get; set; } + public string Text { get; set; } + public MilestoneDto Milestone { get; set; } + public Collection Labels { get; set; } +} + +public class MilestoneDto : EntityDto +{ + public string Name { get; set; } + public bool IsClosed { get; set; } +} + +public class LabelDto : EntityDto +{ + public string Name { get; set; } + public string Color { get; set; } +} +```` + +#### Inputs + +* **Do not** define any property in an input DTO that is not used in the service class. +* **Do not** share input DTOs between application service methods. +* **Do not** inherit an input DTO class from another one. + * **May** inherit from an abstract base DTO class and share some properties between different DTOs in that way. However, should be very careful in that case because manipulating the base DTO would effect all related DTOs and service methods. Avoid from that as a good practice. + +#### Methods + +* **Do** define service methods as asynchronous with **Async** postfix. +* **Do not** repeat the entity name in the method names. + * Example: Define `GetAsync(...)` instead of `GetProductAsync(...)` in the `IProductAppService`. + +##### Getting A Single Entity + +* **Do** use the `GetAsync` **method name**. +* **Do** get Id with a **primitive** method parameter. +* Return the **detailed DTO**. Example: + +````C# +Task GetAsync(Guid id); +```` + +##### Getting A List Of Entities + +* **Do** use the `GetListAsync` **method name**. +* **Do** get a single DTO argument for **filtering**, **sorting** and **paging** if necessary. + * **Do** implement filters optional where possible. + * **Do** implement sorting & paging properties as optional and provide default values. + * **Do** limit maximum page size (for performance reasons). +* **Do** return a list of **detailed DTO**s. Example: + +````C# +Task> GetListAsync(QuestionListQueryDto queryDto); +```` + +##### Creating A New Entity + +* **Do** use the `CreateAsync` **method name**. +* **Do** get a **specialized input** DTO to create the entity. +* **Do** use **data annotations** for input validation. + * Share constants between domain wherever possible (via constants defined in the **domain shared** package). +* **Do** return **the detailed** DTO for new created entity. +* **Do** only require the **minimum** info to create the entity but provide possibility to set others as optional properties. + +Example **method**: + +````C# +Task CreateAsync(CreateQuestionDto questionDto); +```` + +The related **DTO**: + +````C# +public class CreateQuestionDto +{ + [Required] + [StringLength(QuestionConsts.MaxTitleLength, MinimumLength = QuestionConsts.MinTitleLength)] + public string Title { get; set; } + + [StringLength(QuestionConsts.MaxTextLength)] + public string Text { get; set; } //Optional + + public Guid? CategoryId { get; set; } //Optional +} +```` + +##### Updating An Existing Entity + +- **Do** use the `UpdateAsync` **method name**. +- **Do** get a **specialized input** DTO to update the entity. +- **Do** get the Id of the entity as a separated primitive parameter. Do not include to the update DTO. +- **Do** use **data annotations** for input validation. + - Share constants between domain wherever possible (via constants defined in the **domain shared** package). +- **Do** return **the detailed** DTO for the updated entity. + +Example: + +````C# +Task UpdateAsync(Guid id, UpdateQuestionDto updateQuestionDto); +```` + +##### Deleting An Existing Entity + +- **Do** use the `DeleteAsync` **method name**. +- **Do** get Id with a **primitive** method parameter. Example: + +````C# +Task DeleteAsync(Guid id); +```` + +##### Other Methods + +* **Can** define additional methods to perform operations on the entity. Example: + +````C# +Task VoteAsync(Guid id, VoteType type); +```` + +This method votes a question and returns the current score of the question. + +### Application Service Implementation + +* **Do** develop the application layer **completely independent from the web layer**. +* **Do** implement application service interfaces in the **application layer**. + * **Do** use the naming convention. Ex: Create `ProductAppService` class for the `IProductAppService` interface. + * **Do** inherit from the `ApplicationService` base class. + +#### Using Repositories + +* **Do** use the specifically designed repositories (like `IProductRepository`). +* **Do not** use generic repositories (like `IRepository`). + +#### Querying Data + +* **Do not** use LINQ/SQL for querying data from database inside the application service methods. It's repository's responsibility to perform LINQ/SQL queries from the data source. + +#### Manipulating / Deleting Entities + +* **Do** always get all the related entities from repositories to perform the operations on them. + +#### Using Other Application Services + +* **Do not** use other application services of the same module/application. Instead; + * Use domain layer to perform the required task. + * Extract a new class and share between the application services to accomplish the code reuse when necessary. +* **Can** use application services of others only if; + * They are parts of another module / microservice. + * The current module has only reference to the application contracts of the used module. + + + diff --git a/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md b/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md new file mode 100644 index 0000000000..2d25ce7f41 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md @@ -0,0 +1,7 @@ +## Data Transfer Objects Best Practices & Conventions + +* **Do** define DTOs in the **application contracts** package. +* **Do** inherit from the pre-built **base DTO classes** where possible and necessary (like `EntityDto`, `CreationAuditedEntityDto`, `AuditedEntityDto`, `FullAuditedEntityDto` and so on). +* **Do** define DTO members with **public getter and setter**. +* **Do** use **data annotations** for **validation** on the properties of DTOs those are inputs of the service. +* **Do** not add any **logic** into DTOs except implementing `IValidatableObject` when necessary. \ No newline at end of file diff --git a/docs/zh-Hans/Best-Practices/Domain-Services.md b/docs/zh-Hans/Best-Practices/Domain-Services.md new file mode 100644 index 0000000000..4ca2115283 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Domain-Services.md @@ -0,0 +1,3 @@ +## Domain Services Best Practices & Conventions + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Best-Practices/Entities.md b/docs/zh-Hans/Best-Practices/Entities.md new file mode 100644 index 0000000000..03843c5278 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Entities.md @@ -0,0 +1,153 @@ +## Entity Best Practices & Conventions + +### Entities + +Every aggregate root is also an entity. So, these rules are valid for aggregate roots too unless aggregate root rules override them. + +- **Do** define entities in the **domain layer**. + +#### Primary Constructor + +* **Do** define a **primary constructor** that ensures the validity of the entity on creation. Primary constructors are used to create a new instance of the entity by the application code. + +- **Do** define primary constructor as `public`, `internal` or `protected internal` based on the requirements. If it's not public, the entity is expected to be created by a domain service. +- **Do** always initialize sub collections in the primary constructor. +- **Do not** generate `Guid` keys inside the constructor. Get it as a parameter, so the calling code will use `IGuidGenerator` to generate a new `Guid` value. + +#### Parameterless Constructor + +- **Do** always define a `protected` parameterless constructor to be compatible with ORMs. + +#### References + +- **Do** always **reference** to other aggregate roots **by Id**. Never add navigation properties to other aggregate roots. + +#### Other Class Members + +- **Do** always define properties and methods as `virtual` (except `private` methods, obviously). Because some ORMs and dynamic proxy tools require it. +- **Do** keep the entity as always **valid** and **consistent** within its own boundary. + - **Do** define properties with `private`, `protected`, `internal ` or `protected internal` setter where it is needed to protect the entity consistency and validity. + - **Do** define `public `, `internal` or `protected internal` (virtual) **methods** to change the properties (with non-public setters) if necessary. + +### Aggregate Roots + +#### Primary Keys + +* **Do** always use a **Id** property for the aggregate root key. +* **Do not** use **composite keys** for aggregate roots. +* **Do** use **Guid** as the **primary key** of all aggregate roots. + +#### Base Class + +* **Do** inherit from the `AggregateRoot` or one of the audited classes (`CreationAuditedAggregateRoot`, `AuditedAggregateRoot` or `FullAuditedAggregateRoot`) based on requirements. + +#### Aggregate Boundary + +* **Do** keep aggregates **as small as possible**. Most of the aggregates will only have primitive properties and will not have sub collections. Consider these as design decisions: + * **Performance** & **memory** cost of loading & saving aggregates (keep in mind that an aggregate is normally loaded & saved as a single unit). Larger aggregates will consume more CPU & memory. + * **Consistency** & **validity** boundary. + +### Example + +#### Aggregate Root + +````C# +public class Issue : FullAuditedAggregateRoot //Using Guid as the key/identifier +{ + public virtual string Title { get; private set; } //Changed using the SetTitle() method + public virtual string Text { get; set; } //Can be directly changed. null values are allowed + public virtual Guid? MilestoneId { get; set; } //Reference to another aggregate root + public virtual bool IsClosed { get; private set; } + public virtual IssueCloseReason? CloseReason { get; private set; } //Just an enum type + public virtual Collection Labels { get; protected set; } //Sub collection + + protected Issue() + { + /* This conctructor 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 + may not work with private constructors. + */ + } + + //Primary constructor + public Issue( + Guid id, //Get Guid value from the calling code + [NotNull] string title, //Indicate that the title can not be null. + string text = null, + Guid? milestoneId = null) //Optional argument + { + Id = id; + Title = Check.NotNullOrWhiteSpace(title, nameof(title)); //Validate + Text = text; + MilestoneId = milestoneId; + + Labels = new Collection(); //Always initialize the collection + } + + public virtual void SetTitle([NotNull] string title) + { + Title = Check.NotNullOrWhiteSpace(title, nameof(title)); //Validate + } + + /* AddLabel & RemoveLabel methods manages the Labels collection + * in a safe way (prevents adding the same label twice) */ + + public virtual void AddLabel(Guid labelId) + { + if (Labels.Any(l => l.LabelId == labelId)) + { + return; + } + + Labels.Add(new IssueLabel(Id, labelId)); + } + + public virtual void RemoveLabel(Guid labelId) + { + Labels.RemoveAll(l => l.LabelId == labelId); + } + + /* Close & ReOpen methods protect the consistency + * of the IsClosed and the CloseReason properties. */ + + public virtual void Close(IssueCloseReason reason) + { + IsClosed = true; + CloseReason = reason; + } + + public virtual void ReOpen() + { + IsClosed = false; + CloseReason = null; + } +} +```` + +#### The Entity + +````C# +public class IssueLabel : Entity +{ + public virtual Guid IssueId { get; private set; } + public virtual Guid LabelId { get; private set; } + + protected IssueLabel() + { + + } + + public IssueLabel(Guid issueId, Guid labelId) + { + IssueId = issueId; + LabelId = labelId; + } +} +```` + +### References + +* Effective Aggregate Design by Vaughn Vernon + http://dddcommunity.org/library/vernon_2011 \ No newline at end of file diff --git a/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md b/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md new file mode 100644 index 0000000000..f150bd6f66 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md @@ -0,0 +1,209 @@ +## Entity Framework Core Integration Best Practices + +> See [Entity Framework Core Integration document](../Entity-Framework-Core.md) for the basics of the EF Core integration. + +- **Do** define a separated `DbContext` interface and class for each module. +- **Do not** rely on lazy loading on the application development. +- **Do not** enable lazy loading for the `DbContext`. + +### DbContext Interface + +- **Do** define an **interface** for the `DbContext` that inherits from `IEfCoreDbContext`. +- **Do** add a `ConnectionStringName` **attribute** to the `DbContext` interface. +- **Do** add `DbSet` **properties** to the `DbContext` interface for only aggregate roots. Example: + +````C# +[ConnectionStringName("AbpIdentity")] +public interface IIdentityDbContext : IEfCoreDbContext +{ + DbSet Users { get; set; } + DbSet Roles { get; set; } +} +```` + +### DbContext class + +* **Do** inherit the `DbContext` from the `AbpDbContext` class. +* **Do** add a `ConnectionStringName` attribute to the `DbContext` class. +* **Do** implement the corresponding `interface` for the `DbContext` class. Example: + +````C# +[ConnectionStringName("AbpIdentity")] +public class IdentityDbContext : AbpDbContext, IIdentityDbContext +{ + public DbSet Users { get; set; } + public DbSet Roles { get; set; } + + public IdentityDbContext(DbContextOptions options) + : base(options) + { + + } + + //code omitted for brevity +} +```` + +### Table Prefix and Schema + +- **Do** add static `TablePrefix` and `Schema` **properties** to the `DbContext` class. Set default value from a constant. Example: + +````C# +public static string TablePrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix; +public static string Schema { get; set; } = AbpIdentityConsts.DefaultDbSchema; +```` + + - **Do** always use a short `TablePrefix` value for a module to create **unique table names** in a shared database. `Abp` table prefix is reserved for ABP core modules. + - **Do** set `Schema` to `null` as default. + +### Model Mapping + +- **Do** explicitly **configure all entities** by overriding the `OnModelCreating` method of the `DbContext`. Example: + +````C# +protected override void OnModelCreating(ModelBuilder builder) +{ + base.OnModelCreating(builder); + + builder.ConfigureIdentity(options => + { + options.TablePrefix = TablePrefix; + options.Schema = Schema; + }); +} +```` + +- **Do not** configure model directly in the `OnModelCreating` method. Instead, create an **extension method** for `ModelBuilder`. Use Configure*ModuleName* as the method name. Example: + +````C# +public static class IdentityDbContextModelBuilderExtensions +{ + public static void ConfigureIdentity( + [NotNull] this ModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new IdentityModelBuilderConfigurationOptions(); + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Users", options.Schema); + //code omitted for brevity + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "UserClaims", options.Schema); + //code omitted for brevity + }); + + //code omitted for brevity + } +} +```` + +* **Do** create a **configuration options** class by inheriting from the `ModelBuilderConfigurationOptions`. Example: + +````C# +public class IdentityModelBuilderConfigurationOptions : ModelBuilderConfigurationOptions +{ + public IdentityModelBuilderConfigurationOptions() + : base(AbpIdentityConsts.DefaultDbTablePrefix, AbpIdentityConsts.DefaultDbSchema) + { + } +} +```` + +### Repository Implementation + +- **Do** **inherit** the repository from the `EfCoreRepository` class and implement the corresponding repository interface. Example: + +````C# +public class EfCoreIdentityUserRepository + : EfCoreRepository, IIdentityUserRepository +{ + public EfCoreIdentityUserRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } +} +```` + +* **Do** use the `DbContext` interface as the generic parameter, not the class. +* **Do** pass the `cancellationToken` to EF Core using the `GetCancellationToken` helper method. Example: + +````C# +public virtual async Task FindByNormalizedUserNameAsync( + string normalizedUserName, + bool includeDetails = true, + CancellationToken cancellationToken = default) +{ + return await DbSet + .IncludeDetails(includeDetails) + .FirstOrDefaultAsync( + u => u.NormalizedUserName == normalizedUserName, + GetCancellationToken(cancellationToken) + ); +} +```` + +`GetCancellationToken` fallbacks to the `ICancellationTokenProvider.Token` to obtain the cancellation token if it is not provided by the caller code. + +- **Do** create a `IncludeDetails` **extension method** for the `IQueryable` for each aggregate root which has **sub collections**. Example: + +````C# +public static IQueryable IncludeDetails( + this IQueryable queryable, + bool include = true) +{ + if (!include) + { + return queryable; + } + + return queryable + .Include(x => x.Roles) + .Include(x => x.Logins) + .Include(x => x.Claims) + .Include(x => x.Tokens); +} +```` + +* **Do** use the `IncludeDetails` extension method in the repository methods just like used in the example code above (see FindByNormalizedUserNameAsync). + +- **Do** override `IncludeDetails` method of the repository for aggregates root which have **sub collections**. Example: + +````C# +protected override IQueryable IncludeDetails(IQueryable queryable) +{ + return queryable.IncludeDetails(); //uses the extension method defined above +} +```` + +### Module Class + +- **Do** define a module class for the Entity Framework Core integration package. +- **Do** add `DbContext` to the `IServiceCollection` using the `AddAbpDbContext` method. +- **Do** add implemented repositories to the options for the `AddAbpDbContext` method. Example: + +````C# +[DependsOn( + typeof(AbpIdentityDomainModule), + typeof(AbpEntityFrameworkCoreModule) + )] +public class AbpIdentityEntityFrameworkCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); + } +} +```` + diff --git a/docs/zh-Hans/Best-Practices/Index.md b/docs/zh-Hans/Best-Practices/Index.md new file mode 100644 index 0000000000..93d6409909 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Index.md @@ -0,0 +1,27 @@ +## Module Development Best Practices & Conventions + +### Introduction + +This document describes the **best practices** and **conventions** for those who want to develop **modules** that satisfy the following specifications: + +* Develop modules that conform to the **Domain Driven Design** patterns & best practices. +* Develop modules with **DBMS and ORM independence**. +* Develop modules that can be used as a **remote service / microservice** as well as being compatible with a **monolithic** application. + +Also, this guide is mostly usable for general **application development**. + +### Guides + +* Overall + * [Module Architecture](Module-Architecture.md) +* Domain Layer + * [Entities](Entities.md) + * [Repositories](Repositories.md) + * [Domain Services](Domain-Services.md) +* Application Layer + * [Application Services](Application-Services.md) + * [Data Transfer Objects](Data-Transfer-Objects.md) +* Data Access + * [Entity Framework Core Integration](Entity-Framework-Core-Integration.md) + * [MongoDB Integration](MongoDB-Integration.md) + diff --git a/docs/zh-Hans/Best-Practices/Module-Architecture.md b/docs/zh-Hans/Best-Practices/Module-Architecture.md new file mode 100644 index 0000000000..f37d60d68b --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Module-Architecture.md @@ -0,0 +1,89 @@ +## Module Architecture Best Practices & Conventions + +### Solution Structure + +* **Do** create a separated Visual Studio solution for every module. +* **Do** name the solution as *CompanyName.ModuleName* (for core ABP modules, it's *Volo.Abp.ModuleName*). +* **Do** develop the module as layered, so it has several packages (projects) those are related to each other. + * Every package has its own module definition file and explicitly declares the dependencies for the depended packages/modules. + +### Layers & Packages + +The following diagram shows the packages of a well-layered module and dependencies of those packages between them: + +![module-layers-and-packages](../images/module-layers-and-packages.jpg) + +The ultimate goal is to allow an application to use the module in a flexible manner. Example applications: + +* **A)** A **monolithic** application; + * Adds references to the **Web** and the **Application** packages. + * Adds a reference to one of the **EF Core** or the **MongoDB** packages based on the preference. + * The result; + * The application **can show UI** of the module. + * It hosts the **application** and **domain** layers in the **same process** (that's why it needs to have a reference to a database integration package). + * This application also **serves** the module's **HTTP API** (since it includes the HttpApi package through the Web package). +* **B)** An application that just serves the module as a **microservice**; + * Adds a reference to **HttpApi** and **Application** packages. + * Adds a reference to one of the **EF Core** or the **MongoDB** packages based on the preference. + * The result; + * The application **can not show UI** of the module since it does not have a reference to the Web package. + * It hosts the **application** and **domain** layers in the **same process** (that's why it needs to have a reference to a database integration package). + * This application **serves** the module's **HTTP API** (as the main goal of the application). +* **C)** An application that shows the module **UI** but does not host the application (just uses it as a remote service that is hosted by the application A or B); + * Adds a reference to the **Web** and the **HttpApi.Client** packages. + * Configures the remote endpoint for the HttpApi.Client package. + * The result; + * The application **can show UI** of the module. + * It does not host the application and domain layers of the module in the same process. Instead, uses it as a **remote service**. + * This application also **serves** the module's **HTTP API** (since it includes the HttpApi package through the Web package). +* **D)** A **client** application (or microservice) that just uses the module as a remote service (that is hosted by the application A, B or C); + * Adds a reference to the **HttpApi.Client** package. + * Configures the remote endpoint for the HttpApi.Client package. + * The result; + * The application can use all the functionality of the module as a **remote client**. + * The application is just a client and **can not serve** the **HTTP API** of the module. + * The application is just a client and **can not show** the **UI** of the module. +* **E**) A proxy application that hosts the HTTP API of the module but just forwards all requests to another application (that is hosted by the application A, B or C); + * Adds a reference to the **HttpApi** and **HttpApi.Client** packages. + * Configures the remote endpoint for the HttpApi.Client package. + * The result; + * The application can use all the functionality of the module as a **remote client**. + * This application also **serves** the module's **HTTP API**, but actually works just like a proxy by redirecting all requests (for the module) to another remote server. + +Next section describes the packages in more details. + +#### Domain Layer + +* **Do** divide the domain layer into two projects: + * **Domain.Shared** package, named as *CompanyName.ModuleName.Domain.Shared*, that contains constants, enums and other types those can be safely shared with the all layers of the module. This package can also be shared to 3rd-party clients. It can not contain entities, repositories, domain services or any other business objects. + * **Domain** package, named as *CompanyName.ModuleName.Domain*, that contains entities, repository interfaces, domain service interfaces and their implementations and other domain objects. + * Domain package depends on the **Domain.Share** package. + +#### Application Layer + +* **Do** divide the application layer into two projects: + * **Application.Contracts** package, named as *CompanyName.ModuleName.Application.Contracts*, that contains application service interfaces and related data transfer objects. + * Application contract package depends on the **Domain.Shared** package. + * **Application** package, named as *CompanyName.ModuleName.Application*, that contains application service implementations. + * Application package depends on the **Domain** and the **Application.Contracts** packages. + +#### Infrastructure Layer + +* **Do** create a separated integration package for each ORM/database integration like Entity Framework Core and MongoDB. + * **Do**, for instance, create a *CompanyName.ModuleName.EntityFrameworkCore* package that abstracts the Entity Framework Core integration. ORM integration packages depend on the **Domain** package. + * **Do not** depend on other layers from the ORM/database integration package. +* **Do** create a separated integration package for each major library that is planned to be replaceable by another library without effecting the other packages. + +#### HTTP Layer + +* **Do** create an **HTTP API** package, named as *CompanyName.ModuleName.HttpApi*, to develop a REST style HTTP API for the module. + * HTTP API package only depends on the **Application.Contracts** package. It does not depend on the Application package. + * **Do** create a Controller for each application service (generally by implementing their interfaces). These controllers uses the application service interfaces to delegate the actions. It just configures routes, HTTP methods and other web related stuffs if needed. +* **Do** create an **HTTP API Client** package, named as *CompanyName.ModuleName.HttpApi.Client*, to provide client services for the HTTP API package. Those client services implement application interfaces as clients to a remote endpoint. + * HTTP API Client package only depends on the **Application.Contracts** package. + * **Do** use dynamic HTTP C# client proxy feature of the ABP framework. + +#### Web Layer + +* Do create a **Web** package, named as *CompanyName.ModuleName.Web*, that contains pages, views, scripts, styles, images and other UI components. + * Web package only depends on the **HttpApi** package. \ No newline at end of file diff --git a/docs/zh-Hans/Best-Practices/MongoDB-Integration.md b/docs/zh-Hans/Best-Practices/MongoDB-Integration.md new file mode 100644 index 0000000000..32eefadd6a --- /dev/null +++ b/docs/zh-Hans/Best-Practices/MongoDB-Integration.md @@ -0,0 +1,201 @@ +## MongoDB Integration + +* Do define a separated `MongoDbContext` interface and class for each module. + +### MongoDbContext Interface + +- **Do** define an **interface** for the `MongoDbContext` that inherits from `IAbpMongoDbContext`. +- **Do** add a `ConnectionStringName` **attribute** to the `MongoDbContext` interface. +- **Do** add `IMongoCollection` **properties** to the `MongoDbContext` interface only for the aggregate roots. Example: + +````C# +[ConnectionStringName("AbpIdentity")] +public interface IAbpIdentityMongoDbContext : IAbpMongoDbContext +{ + IMongoCollection Users { get; } + IMongoCollection Roles { get; } +} +```` + +### MongoDbContext class + +- **Do** inherit the `MongoDbContext` from the `AbpMongoDbContext` class. +- **Do** add a `ConnectionStringName` attribute to the `MongoDbContext` class. +- **Do** implement the corresponding `interface` for the `MongoDbContext` class. Example: + +```c# +[ConnectionStringName("AbpIdentity")] +public class AbpIdentityMongoDbContext : AbpMongoDbContext, IAbpIdentityMongoDbContext +{ + public IMongoCollection Users => Collection(); + public IMongoCollection Roles => Collection(); + + //code omitted for brevity +} +``` + +### Collection Prefix + +- **Do** add static `CollectionPrefix` **property** to the `DbContext` class. Set default value from a constant. Example: + +```c# +public static string CollectionPrefix { get; set; } = AbpIdentityConsts.DefaultDbTablePrefix; +``` + +Used the same constant defined for the EF Core integration table prefix in this example. + +- **Do** always use a short `CollectionPrefix` value for a module to create **unique collection names** in a shared database. `Abp` collection prefix is reserved for ABP core modules. + +### Collection Mapping + +- **Do** explicitly **configure all aggregate roots** by overriding the `CreateModel` method of the `MongoDbContext`. Example: + +```c# +protected override void CreateModel(IMongoModelBuilder modelBuilder) +{ + base.CreateModel(modelBuilder); + + modelBuilder.ConfigureIdentity(options => + { + options.CollectionPrefix = CollectionPrefix; + }); +} +``` + +- **Do not** configure model directly in the `CreateModel` method. Instead, create an **extension method** for the `IMongoModelBuilder`. Use Configure*ModuleName* as the method name. Example: + +```c# +public static class AbpIdentityMongoDbContextExtensions +{ + public static void ConfigureIdentity( + this IMongoModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new IdentityMongoModelBuilderConfigurationOptions(); + + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "Users"; + }); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "Roles"; + }); + } +} +``` + +- **Do** create a **configuration options** class by inheriting from the `MongoModelBuilderConfigurationOptions`. Example: + +```c# +public class IdentityMongoModelBuilderConfigurationOptions + : MongoModelBuilderConfigurationOptions +{ + public IdentityMongoModelBuilderConfigurationOptions() + : base(AbpIdentityConsts.DefaultDbTablePrefix) + { + } +} +``` + +* **Do** explicitly configure `BsonClassMap` for all entities. Create a static method for this purpose. Example: + +````C# +public static class AbpIdentityBsonClassMap +{ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + BsonClassMap.RegisterClassMap(map => + { + map.AutoMap(); + map.ConfigureExtraProperties(); + }); + + BsonClassMap.RegisterClassMap(map => + { + map.AutoMap(); + }); + }); + } +} +```` + +`BsonClassMap` works with static methods. So, it is only needed to configure entities once in an application. `OneTimeRunner` guarantees that it runs in a thread safe manner and only once in the application life. Such a mapping above ensures that unit test properly run. This code will be called by the **module class** below. + +### Repository Implementation + +- **Do** **inherit** the repository from the `MongoDbRepository` class and implement the corresponding repository interface. Example: + +```c# +public class MongoIdentityUserRepository + : MongoDbRepository, + IIdentityUserRepository +{ + public MongoIdentityUserRepository( + IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } +} +``` + +- **Do** pass the `cancellationToken` to the MongoDB Driver using the `GetCancellationToken` helper method. Example: + +```c# +public async Task FindByNormalizedUserNameAsync( + string normalizedUserName, + bool includeDetails = true, + CancellationToken cancellationToken = default) +{ + return await GetMongoQueryable() + .FirstOrDefaultAsync( + u => u.NormalizedUserName == normalizedUserName, + GetCancellationToken(cancellationToken) + ); +} +``` + +`GetCancellationToken` fallbacks to the `ICancellationTokenProvider.Token` to obtain the cancellation token if it is not provided by the caller code. + +* **Do** ignore the `includeDetails` parameters for the repository implementation since MongoDB loads the aggregate root as a whole (including sub collections) by default. +* **Do** use the `GetMongoQueryable()` method to obtain an `IQueryable` to perform queries wherever possible. Because; + * `GetMongoQueryable()` method automatically uses the `ApplyDataFilters` method to filter the data based on the current data filters (like soft delete and multi-tenancy). + * Using `IQueryable` makes the code as much as similar to the EF Core repository implementation and easy to write and read. +* **Do** implement data filtering if it is not possible to use the `GetMongoQueryable()` method. + +### Module Class + +- **Do** define a module class for the MongoDB integration package. +- **Do** add `MongoDbContext` to the `IServiceCollection` using the `AddMongoDbContext` method. +- **Do** add implemented repositories to the options for the `AddMongoDbContext` method. Example: + +```c# +[DependsOn( + typeof(AbpIdentityDomainModule), + typeof(AbpUsersMongoDbModule) + )] +public class AbpIdentityMongoDbModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + AbpIdentityBsonClassMap.Configure(); + + context.Services.AddMongoDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); + } +} +``` + +Notice that this module class also calls the static `BsonClassMap` configuration method defined above. \ No newline at end of file diff --git a/docs/zh-Hans/Best-Practices/Repositories.md b/docs/zh-Hans/Best-Practices/Repositories.md new file mode 100644 index 0000000000..c0058df433 --- /dev/null +++ b/docs/zh-Hans/Best-Practices/Repositories.md @@ -0,0 +1,92 @@ +## Repository Best Practices & Conventions + +### Repository Interfaces + +* **Do** define repository interfaces in the **domain layer**. +* **Do** define a repository interface (like `IIdentityUserRepository`) and create its corresponding implementations for **each aggregate root**. + * **Do** always use the created repository interface from the application code. + * **Do not** use generic repository interfaces (like `IRepository`) from the application code. + * **Do not** use `IQueryable` features in the application code (domain, application... layers). + +For the example aggregate root: + +````C# +public class IdentityUser : AggregateRoot +{ + //... +} +```` + +Define the repository interface as below: + +````C# +public interface IIdentityUserRepository : IBasicRepository +{ + //... +} +```` + +* **Do not** inherit the repository interface from the `IRepository` interface. Because it inherits the `IQueryable` and the repository should not expose `IQueryable` to the application. +* **Do** inherit the repository interface from `IBasicRepository` (as normally) or a lower-featured interface, like `IReadOnlyRepository` (if it's needed). +* **Do not** define repositories for entities those are **not aggregate roots**. + +### Repository Methods + +* **Do** define all repository methods as **asynchronous**. +* **Do** add an **optional** `cancellationToken` parameter to every method of the repository. Example: + +````C# +Task FindByNormalizedUserNameAsync( + [NotNull] string normalizedUserName, + CancellationToken cancellationToken = default +); +```` + +* **Do** create a **synchronous extension** method for each asynchronous repository method. Example: + +````C# +public static class IdentityUserRepositoryExtensions +{ + public static IdentityUser FindByNormalizedUserName( + this IIdentityUserRepository repository, + [NotNull] string normalizedUserName) + { + return AsyncHelper.RunSync( + () => repository.FindByNormalizedUserNameAsync(normalizedUserName) + ); + } +} +```` + +This will allow synchronous code to use the repository methods easier. + +* **Do** add an optional `bool includeDetails = true` parameter (default value is `true`) for every repository method which returns a **single entity**. Example: + +````C# +Task FindByNormalizedUserNameAsync( + [NotNull] string normalizedUserName, + bool includeDetails = true, + CancellationToken cancellationToken = default +); +```` + +This parameter will be implemented for ORMs to eager load sub collections of the entity. + +* **Do** add an optional `bool includeDetails = false` parameter (default value is `false`) for every repository method which returns a **list of entities**. Example: + +````C# +Task> GetListByNormalizedRoleNameAsync( + string normalizedRoleName, + bool includeDetails = false, + CancellationToken cancellationToken = default +); +```` + +* **Do not** create composite classes to combine entities to get from repository with a single method call. Examples: *UserWithRoles*, *UserWithTokens*, *UserWithRolesAndTokens*. Instead, properly use `includeDetails` option to add all details of the entity when needed. +* **Avoid** to create projection classes for entities to get less property of an entity from the repository. Example: Avoid to create BasicUserView class to select a few properties needed for the use case needs. Instead, directly use the aggregate root class. However, there may be some exceptions for this rule, where: + * Performance is so critical for the use case and getting the whole aggregate root highly impacts the performance. + +### See Also + +* [Entity Framework Core Integration](Entity-Framework-Core-Integration.md) +* [MongoDB Integration](MongoDB-Integration.md) diff --git a/docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/Post.md b/docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/Post.md new file mode 100644 index 0000000000..437ee87387 --- /dev/null +++ b/docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/Post.md @@ -0,0 +1,174 @@ +### Introduction +For a while, we were working to design a new major version of the ASP.NET Boilerplate framework. Now, it’s time to share it with the community. We are too excited and we believe that you are too. + +#### Naming +The name of the framework remains same, except we will call it only as “ABP” instead of “ASP.NET Boilerplate”. Because, the “boilerplate” word leads to misunderstandings and does not reflect that it is a framework (instead of some boilerplate code). We continue to use the “ABP” name since it’s the successor of the current ASP.NET Boilerplate framework, except it’s a rewrite. + +### How To Start + +We have created a startup template. You can just create a new project from https://abp.io/Templates and start your development. For more information, visit [abp.io](https://abp.io). + +### Why A Complete Rewrite? +Why we spent our valuable time to rewrite it from scratch instead of incremental changes and improvements. Why? + +#### ASP.NET Core +When we first introduced the ABP framework, it was 2013 (5 years ago)! There was no .Net Core & ASP.NET Core and there was no Angular2+. They were all developed from scratch after ABP’s release. + +ASP.NET Core introduced many built-in solutions (extension libraries) for dependency injection, logging, caching, localization, configuration and so on. These are actually independent from the ASP.NET Core and usable for any type of application. + +We were using 3rd-party libraries and our own solutions for these requirements. We immediately integrated to ASP.NET Core features once they were released. But that was an integration, instead of building the ABP framework on top of these extension libraries. For instance, current ASP.NET Boilerplate still depends on Castle Windsor for dependency injection even it’s integrated to ASP.NET Core’s DI system. + +We wanted to depend on these new extension libraries instead of 3rd-party and custom solutions and this changes fundamental structures of the framework. + +#### Self Modularization +While current ABP is already modular itself and consists of dozens of packages, we still wanted to split the functionalities to more fine grained nuget packages. + +For example, the core Abp package contains many features like DDD classes, auditing, authorization, background jobs, event bus, json serialization, localization, multi-tenancy, threading, timing and so on… We wanted to split all these functionality into their own packages and make them optional. + +#### Dropping Support for Legacy Technologies +Yes, the new ABP framework will not support ASP.NET MVC 5.x, Entity Framework 6.x and other legacy technologies. + +These legacy technologies are maintained by Microsoft but no new feature is being added. So, if you are still using these technologies, you can continue with the current ASP.NET Boilerplate framework. We will continue to maintain it, fix bugs and will add new features. + +Dropping support for these legacy libraries will improve our development speed (since we currently duplicate our work for some features) and concentrate on the .Net Core & ASP.NET Core. + +The new ABP framework will be based on .net standard. So, it’s still possible to use full .net framework or .net core with the new ABP framework. + +### Goals +We have learnt much from the community and had experience of developing the current ASP.NET Boilerplate framework. New ABP framework has significant and exciting goals. + +#### Application Modularity +The first goal is to provide a good infrastructure to develop application modules. We think a module as a set of application features with its own database, its own entities, services, APIs, UI pages, components and so on. + +We will create a module market which will contain free & paid application modules. You will also be able to publish your own modules on the market. More information will be coming soon. + +#### Microservices +We are designing the new ABP framework to be ready to develop microservices and communicate them to each other. + +We are designing application modules so that they can be separately deployable as microservices or they can be embedded into a monolithic application. + +We are creating a [specification / best practice documentation](https://github.com/abpframework/abp/blob/master/docs/Best-Practices/Index.md) for that. + +#### Theming and UI Composition +The new ABP framework will provide a theming infrastructure based on the latest Twitter Bootstrap 4.x. We developed a basic theme that only uses the plain Bootstrap 4.x styling. It’s free and open source. We are also developing premium & paid themes. + +UI Composition is one of the main goals. For this purpose, theme system will provide menus, toolbars and other extensible areas to allow other modules to contribute. + +#### ORM/Database Independence & MongoDB Integration +While current ASP.NET Boilerplate framework has implemented the repository pattern for ORM/Database independence, identity integration module (Abp.Zero* packages) has never worked well with ORMs other than EF. + +With the new ABP framework, the ultimate goal is completely abstract underlying data store system and develop modules EF Core independent. + +We embrace the MongoDB as a first-class citizen database and designing entities and repositories without any relational database or ORM assumption. + +#### More Extensibility +New ABP framework provides more extensibility points and overriding capabilities for built-in services. + +### Some Features +In this section, I will introduce some exciting new features of the new ABP framework. + +#### Bootstrap Tag Helpers +We are creating a library to wrap twitter bootstrap 4.x elements/components into tag helpers. Example: + +````C# + + + + Card title + +

+ This is a sample card component built by ABP bootstrap + card tag helper. ABP has tag helper wrappers for most of + the bootstrap components. +

+
+ Go somewhere → +
+
+```` + +"abp-*" tags are ABP tag helpers to simplify writing HTML for Bootstrap 4.x. + +#### Dynamic Forms +Dynamic forms tag helper allows you to dynamically create forms for given model classes. Example: + +````C# + +```` + +Output: + +![dynamic-forms](dynamic-forms.png) + +It currently supports most used input types and more in the development. + +#### Virtual File System +Virtual File System allows you to embed views, pages, components, javascript, css, json and other type of files into your module assembly/package (dll) and use your assembly in any application. Your virtual files behave just like physical files in the containing application with complete ASP.NET Core Integration. + +Read more [about the Virtual File System](https://medium.com/volosoft/designing-modularity-on-asp-net-core-virtual-file-system-2dd2cc2078bd) and see [its documentation](https://github.com/abpframework/abp/blob/master/docs/Virtual-File-System.md). + +#### Dynamic Bundling & Minification System +Dynamic bundling & minification system works on the virtual file system and allows modules to create, modify and contribute to bundles in a modular, dynamic and powerful way. An example: + +````C# + + + + + +```` + +This code creates a new style bundle on the fly by including bootstrap (and its dependencies if there are) and two more css files. These files are bundled & minified on production environment, but will be added individually on the development environment. + +See [the documentation](https://github.com/abpframework/abp/blob/master/docs/AspNetCore/Bundling-Minification.md) for more. + +#### Distributed Event Bus +In current ABP, there is an IEventBus service to trigger and handle events inside the application. In addition to this local event bus, we are creating a distributed event bus abstraction (and RabbitMQ integration) to implement distributed messaging patterns. + +#### Dynamic C# HTTP Client Proxies +ABP was already creating dynamic javascript proxies for all HTTP APIs. This feature does also exists in the new ABP framework. In addition, it now can create dynamic C# proxies for all HTTP APIs. + +### Future Works +All the stuffs mentioned above are already in development. However, we haven’t started some concepts yet. + +#### Single Page Applications +We designed the new framework SPAs in mind. However, we haven’t tried it with any SPA framework and we haven’t prepared a startup template for it yet. + +### What About ASP.NET Boilerplate (Current Version) and ASP.NET Zero? + +We have dedicated development & support teams actively working on the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) and [ASP.NET Zero](https://aspnetzero.com/) projects. These projects have a big community and we are also getting contributions from the community. + +We will continue to make enhancements, add new features and fix bugs for these projects for a long time. So, you can safely continue to use them. + +### Is New ABP Production Ready? +No, not yet. Our first goal is to make fundamental features stable then incrementally complete other features. + +We will frequently release new versions and every new version will probably have breaking changes. We will write breaking changes on the release notes. + +We currently define it experimental. But we hope that this will not continue for a long time. We can not declare a date yet, follow our releases. + +### Packages & Versioning +New ABP framework will start with v1.0 instead of following current ASP.NET Boilerplate's version to reflect the fact that it’s a rewrite. + +We will frequently [release](https://github.com/abpframework/abp/releases) it. You can expect many breaking changes until v1.0. Starting with the v1.0, we will pay attention to not introduce breaking changes in 1.x releases. + +Current ABP’s package names start with [Abp](https://www.nuget.org/packages/Abp) prefix (like Abp.EntityFrameworkCore). New package names start with [Volo.Abp](https://www.nuget.org/packages/Volo.Abp.Core) prefix (like Volo.Abp.EntityFrameworkCore). + +### Which One Should I Start With? +If you are creating a new project, we suggest to continue with the current ASP.NET Boilerplate framework since it’s very mature, feature rich and production ready. + +If you are open to breaking changes and want to have experience on the new framework, you can start with the new ABP. We don’t suggest it yet for projects with close deadlines and go to the production in a short term. + +### Contribution +Just like the current ABP framework, the new framework is available for your contribution. + +* You can send pull requests for code or documentation. +* You can write blog posts or tutorials about it. +* You can try it and share your experiences. +* You can create enhancement and feature requests. +* You can report bugs and other issues. + +### Communication / Links +* **Official web site**: [abp.io](https://abp.io) +* **Github**: [github.com/abpframework](https://github.com/abpframework) +* **Twitter**: [@abpframework](https://twitter.com/abpframework) \ No newline at end of file diff --git a/docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/dynamic-forms.png b/docs/zh-Hans/Blog-Posts/2018-09-24-Announcement/dynamic-forms.png new file mode 100644 index 0000000000000000000000000000000000000000..d0c3b8f25098e6018aca587bc5ccc2ac146cca3e GIT binary patch literal 8842 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 + } +} +```` + +The section below explains the conventions and configurations. + +#### Inherently Registered Types + +Some specific types are registered to dependency injection by default. Examples: + +* Module classes are registered as singleton. +* MVC controllers (inherit ``Controller`` or ``AbpController``) are registered as transient. +* MVC page models (inherit ``PageModel`` or ``AbpPageModel``) are registered as transient. +* MVC view components (inherit ``ViewComponent`` or ``AbpViewComponent``) are registered as transient. +* Application services (implement ``IApplicationService`` interface or inherit ``ApplicationService`` class) are registered as transient. +* Repositories (implement ``IRepository`` interface) are registered as transient. +* Domain services (implement ``IDomainService`` interface) are registered as transient. + +Example: + +````C# +public class BlogPostAppService : ApplicationService +{ +} +```` + +``BlogPostAppService`` is automatically registered with transient lifetime since it's derived from a known base class. + +#### Dependency Interfaces + +If you implement these interfaces, your class is registered to dependency injection automatically: + +* ``ITransientDependency`` to register with transient lifetime. +* ``ISingletonDependency`` to register with singleton lifetime. +* ``IScopedDependency`` to register with scoped lifetime. + +Example: + +````C# +public class TaxCalculator : ITransientDependency +{ +} +```` + +``TaxCalculator`` is automatically registered with a transient lifetime since it implements ``ITransientDependency``. + +#### Dependency Attribute + +Another way of configuring a service for dependency injection is to use ``DependencyAttribute``. It has the following properties: + +* ``Lifetime``: Lifetime of the registration: ``Singleton``, ``Transient`` or ``Scoped``. +* ``TryRegister``: Set ``true`` to register the service only it's not registered before. Uses TryAdd... extension methods of IServiceCollection. +* ``ReplaceServices``: Set ``true`` to replace services if they are already registered before. Uses Replace extension method of IServiceCollection. + +Example: + +````C# +[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] +public class TaxCalculator +{ + +} + +```` + +``Dependency`` attribute has a higher priority than other dependency interfaces if it defines the ``Lifetime`` property. + +#### ExposeServices Attribute + +``ExposeServicesAttribute`` is used to control which services are provided by the related class. Example: + +````C# +[ExposeServices(typeof(ITaxCalculator))] +public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency +{ + +} +```` + +``TaxCalculator`` class only exposes ``ITaxCalculator`` interface. That means you can only inject ``ITaxCalculator``, but can not inject ``TaxCalculator`` or ``ICalculator`` in your application. + +#### Exposed Services by Convention + +If you do not specify which services to expose, ABP expose services by convention. So taking the ``TaxCalculator`` defined above: + +* The class itself is exposed by default. That means you can inject it by ``TaxCalculator`` class. +* Default interfaces are exposed by default. Default interfaces are determined by naming convention. In this example, ``ICalculator`` and ``ITaxCalculator`` are default interfaces of ``TaxCalculator``, but ``ICanCalculate`` is not. + +#### Combining All Together + +Combining attributes and interfaces is possible as long as it's meaningful. + +````C# +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(ITaxCalculator))] +public class TaxCalculator : ITaxCalculator, ITransientDependency +{ + +} +```` + +#### Manually Registering + +In some cases, you may need to register a service to the `IServiceCollection` manually, especially if you need to use custom factory methods or singleton instances. In that case, you can directly add services just as [Microsoft documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection) describes. Example: + +````C# +public class BlogModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + //Register an instance as singleton + context.Services.AddSingleton(new TaxCalculator(taxRatio: 0.18)); + + //Register a factory method that resolves from IServiceProvider + context.Services.AddScoped(sp => sp.GetRequiredService()); + } +} +```` + +### Injecting Dependencies + +There are three common ways of using a service that has already been registered. + +#### Contructor Injection + +This is the most common way of injecting a service into a class. For example: + +````C# +public class TaxAppService : ApplicationService +{ + private readonly ITaxCalculator _taxCalculator; + + public TaxAppService(ITaxCalculator taxCalculator) + { + _taxCalculator = taxCalculator; + } + + public void DoSomething() + { + //...use _taxCalculator... + } +} +```` + +``TaxAppService`` gets ``ITaxCalculator`` in it's constructor. The dependency injection system automatically provides the requested service at runtime. + +Constructor injection is preffered way of injecting dependencies to a class. In that way, the class can not be constructed unless all constructor-injected dependencies are provided. Thus, the class explicitly declares it's required services. + +#### Property Injection + +Property injection is not supported by Microsoft Dependency Injection library. However, ABP can integrate with 3rd-party DI providers ([Autofac](https://autofac.org/), for example) to make property injection possible. Example: + +````C# +public class MyService : ITransientDependency +{ + public ILogger Logger { get; set; } + + public MyService() + { + Logger = NullLogger.Instance; + } + + public void DoSomething() + { + //...use Logger to write logs... + } +} +```` + +For a property-injection dependency, you declare a public property with public setter. This allows the DI framework to set it after creating your class. + +Property injected dependencies are generally considered as **optional** dependencies. That means the service can properly work without them. ``Logger`` is such a dependency, ``MyService`` can continue to work without logging. + +To make the dependency properly optional, we generally set a default/fallback value to the dependency. In this sample, NullLogger is used as fallback. Thus, ``MyService`` can work but does not write logs if DI framework or you don't set Logger property after creating ``MyService``. + +One restriction of property injection is that you cannot use the dependency in your constructor, since it's set after the object construction. + +Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class. + +#### Resolve Service from IServiceProvider + +You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below: + +````C# +public class MyService : ITransientDependency +{ + private readonly IServiceProvider _serviceProvider; + + public MyService(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public void DoSomething() + { + var taxCalculator = _serviceProvider.GetService(); + //... + } +} +```` + +#### Releasing/Disposing Services + +If you used a constructor or property injection, you don't need to be concerned about releasing the service's resources. However, if you have resolved a service from ``IServiceProvider``, you might, in some cases, need to take care about releasing the service resources. + +ASP.NET Core releases all services at the end of a current HTTP request, even if you directly resolved from ``IServiceProvider`` (assuming you injected IServiceProvider). But, there are several cases where you may want to release/dispose manually resolved services: + +* Your code is executed outside of AspNet Core request and the executer hasn't handled the service scope. +* You only have a reference to the root service provider. +* You may want to immediately release & dispose services (for example, you may creating too many services with big memory usage and don't want to overuse memory). + +In any case, you can use such a 'using' code block to safely and immediately release services: + +````C# +using (var scope = _serviceProvider.CreateScope()) +{ + var service1 = scope.ServiceProvider.GetService(); + var service2 = scope.ServiceProvider.GetService(); +} +```` + +Both services are released when the created scope is disposed (at the end of the using block). + +### See Also + +* [ASP.NET Core Dependency Injection Best Practices, Tips & Tricks](https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96) diff --git a/docs/zh-Hans/Entities.md b/docs/zh-Hans/Entities.md new file mode 100644 index 0000000000..89688d58c8 --- /dev/null +++ b/docs/zh-Hans/Entities.md @@ -0,0 +1,176 @@ +## Entities + +Entities are one of the core concepts of DDD (Domain Driven Design). Eric Evans describe it as "*An object that is not fundamentally defined by its attributes, but rather by a thread of continuity and identity*". + +An entity is generally mapped to a table in a relational database. + +### Entity Class + +Entities are derived from `Entity` class as shown below: + +```C# +public class Person : Entity +{ + public string Name { get; set; } + + public DateTime CreationTime { get; set; } + + public Person() + { + CreationTime = DateTime.Now; + } +} +``` + +> If you do not want derive your entity from the base `Entity` class, you can directly implement `IEntity` interface. + +`Entity` class just defines an `Id` property with the given primary **key type**, which is `int` in the sample above. It can be other types like `string`, `Guid`, `long` or whatever you need. + +Entity class also overrides the **equality** operator (==) to easily check if two entities are equal (they are equals if they are same entity type and their Ids are equals). + +#### Entities with Composite Keys + +Some entities may need to have **composite keys**. In that case, you can derive your entity from the non-generic `Entity` class. Example: + +````C# +public class UserRole : Entity +{ + public Guid UserId { get; set; } + + public Guid RoleId { get; set; } + + public DateTime CreationTime { get; set; } + + public Phone() + { + + } + + public override object[] GetKeys() + { + return new object[] { UserId, RoleId }; + } +} +```` + +For the example above, the composite key is composed of `UserId` and `RoleId`. For a relational database, it is the composite primary key of the related table. + +Entities with composite keys should implement the `GetKeys()` method as shown above. + +Notice that you also need to define keys of the entity in your **object-to-relational mapping** (ORM) configuration. + +> Composite primary keys has a restriction with repositories. Since it has not known Id property, you can not use `IRepository` for these entities. However, you can always use `IRepository`. See repository documentation (TODO: link) for more. + +### AggregateRoot Class + +"*Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate.*" (see the [full description](http://martinfowler.com/bliki/DDD_Aggregate.html)) + +`AggregateRoot` class extends the `Entity` class. So, it also has an `Id` property by default. + +> Notice that ABP creates default repositories only for aggregate roots by default. However, it's possible to include all entities. See repository documentation (TODO: link) for more. + +ABP does not force you to use aggregate roots, you can in fact use the `Entity` class as defined before. However, if you want to implement DDD and want to create aggregate root classes, there are some best practices you may want to consider: + +* An aggregate root is responsible to preserve it's own integrity. This is also true for all entities, but aggregate root has responsibility for it's sub entities too. So, the aggregate root must always be in a valid state. +* An aggregate root can be referenced by it's Id. Do not reference it by it's navigation property. +* An aggregate root is treated as a single unit. It's retrieved and updated as a single unit. It's generally considered as a transaction boundary. +* Work with sub-entities over the aggregate root- do not modify them independently. + +#### Aggregate Example + +This is a full sample of an aggregate root with a related sub-entity collection: + +````C# +public class Order : AggregateRoot +{ + public virtual string ReferenceNo { get; protected set; } + + public virtual int TotalItemCount { get; protected set; } + + public virtual DateTime CreationTime { get; protected set; } + + public virtual List OrderLines { get; protected set; } + + protected Order() + { + + } + + public Order(Guid id, string referenceNo) + { + Check.NotNull(referenceNo, nameof(referenceNo)); + + Id = id; + ReferenceNo = referenceNo; + + OrderLines = new List(); + } + + public void AddProduct(Guid productId, int count) + { + if (count <= 0) + { + throw new ArgumentException( + "You can not add zero or negative count of products!", + nameof(count) + ); + } + + var existingLine = OrderLines.FirstOrDefault(ol => ol.ProductId == productId); + + if (existingLine == null) + { + OrderLines.Add(new OrderLine(this.Id, productId, count)); + } + else + { + existingLine.ChangeCount(existingLine.Count + count); + } + + TotalItemCount += count; + } +} + +public class OrderLine : Entity +{ + public virtual Guid OrderId { get; protected set; } + + public virtual Guid ProductId { get; protected set; } + + public virtual int Count { get; protected set; } + + protected OrderLine() + { + + } + + internal OrderLine(Guid orderId, Guid productId, int count) + { + OrderId = orderId; + ProductId = productId; + Count = count; + } + + internal void ChangeCount(int newCount) + { + Count = newCount; + } +} +```` + +> If you do not want to derive your aggregate root from the base `AggregateRoot` class, you can directly implement the `IAggregateRoot` interface. + +`Order` is an **aggregate root** with `Guid` type `Id` property. It has a collection of `OrderLine` entities. `OrderLine` is another entity with a composite primary key (`OrderLine` and ` ProductId`). + +While this example may not implement all the best practices of an aggregate root, it still follows good practices: + +* `Order` has a public constructor that takes **minimal requirements** to construct an `Order` instance. So, it's not possible to create an order without an id and reference number. The **protected/private** constructor is only necessary to **deserialize** the object while reading from a data source. +* `OrderLine` constructor is internal, so it is only allowed to be created by the domain layer. It's used inside of the `Order.AddProduct` method. +* `Order.AddProduct` implements the business rule to add a product to an order. +* All properties have `protected` setters. This is to prevent the entity from arbitrary changes from outside of the entity. For exmple, it would be dangerous to set `TotalItemCount` without adding a new product to the order. It's value is maintained by the `AddProduct` method. + +ABP does not force you to apply any DDD rule or patterns. However, it tries to make it possible and easier when you do want to apply them. The documentation also follows the same principle. + +#### Aggregate Roots with Composite Keys + +While it's not common (and not suggested) for aggregate roots, it is in fact possible to define composite keys in the same way as defined for the mentioned entities above. Use non-generic `AggregateRoot` base class in that case. diff --git a/docs/zh-Hans/Entity-Framework-Core.md b/docs/zh-Hans/Entity-Framework-Core.md new file mode 100644 index 0000000000..1d1a5d5f9d --- /dev/null +++ b/docs/zh-Hans/Entity-Framework-Core.md @@ -0,0 +1,107 @@ +## Entity Framework Core Integration + +This document explains how to integrate EF Core as an ORM provider to ABP based applications and how to configure it. + +### Installation + +`Volo.Abp.EntityFrameworkCore` is the main nuget package for the EF Core integration. Install it to your project (for a layered application, to your data/infrastructure layer): + +```` +Install-Package Volo.Abp.EntityFrameworkCore +```` + +Then add `AbpEntityFrameworkCoreModule` module dependency to your [module](Module-Development-Basics.md): + +````C# +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + public class MyModule : AbpModule + { + //... + } +} +```` + +### Creating DbContext + +You can create your DbContext as you normally do. It should be derived from `AbpDbContext` as shown below: + +````C# +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace MyCompany.MyProject +{ + public class MyDbContext : AbpDbContext + { + //...your DbSet properties + + public MyDbContext(DbContextOptions options) + : base(options) + { + } + } +} +```` + +### Registering DbContext To Dependency Injection + +Use `AddAbpDbContext` method in your module to register your DbContext class for [dependency injection](Dependency-Injection.md) system. + +````C# +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + public class MyModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(); + + //... + } + } +} +```` + +#### Add Default Repositories + +ABP can automatically create repositories (TODO: link) for the entities in your DbContext. Just use `AddDefaultRepositories()` option on registration: + +````C# +services.AddAbpDbContext(options => +{ + options.AddDefaultRepositories(); +}); +```` + +This will create a repository for each aggreate root entity (classes derived from AggregateRoot) by default. If you want to create repositories for other entities too, then set `includeAllEntities` to `true`: + +````C# +services.AddAbpDbContext(options => +{ + options.AddDefaultRepositories(includeAllEntities: true); +}); +```` + +Then you can inject and use `IRepository` or `IQueryableRepository` in your services. + +#### Add Custom Repositories + +TODO... + +#### Set Base DbContext Class or Interface for Default Repositories + +... + +#### Replace Other Repository + +... \ No newline at end of file diff --git a/docs/zh-Hans/Exception-Handling.md b/docs/zh-Hans/Exception-Handling.md new file mode 100644 index 0000000000..896f26d723 --- /dev/null +++ b/docs/zh-Hans/Exception-Handling.md @@ -0,0 +1,302 @@ +## Exception Handling + +ABP provides a built-in infrastructure and offers a standard model for handling exceptions in a web application. + +* Automatically **handles all exceptions** and sends a standard **formatted error message** to the client for an API/AJAX request. +* Automatically hides **internal infrastructure errors** and returns a standard error message. +* Provides a configurable way to **localize** exception messages. +* Automatically maps standard exceptions to **HTTP status codes** and provides a configurable option to map these to custom exceptions. + +### Automatic Exception Handling + +`AbpExceptionFilter` handles an exception if **any of the following conditions** are meet: + +* Exception is thrown by a **controller action** which returns an **object result** (not a view result). +* The request is an AJAX request (`X-Requested-With` HTTP header value is `XMLHttpRequest`). +* Client explicitly accepts the `application/json` content type (via `accept` HTTP header). + +If the exception is handled it's automatically **logged** and a formatted **JSON message** is returned to the client. + +#### Error Message Format + +Error Message is an instance of the `RemoteServiceErrorResponse` class. The simplest error JSON has a **message** property as shown below: + +````json +{ + "error": { + "message": "This topic is locked and can not add a new message" + } +} +```` + +There are **optional fields** those can be filled based upon the exception that has occured. + +##### Error Code + +Error **code** is an optional and unique string value for the exception. Thrown `Exception` should implement the `IHasErrorCode` interface to fill this field. Example JSON value: + +````json +{ + "error": { + "code": "App:010042", + "message": "This topic is locked and can not add a new message" + } +} +```` + +Error code can also be used to localize the exception and customize the HTTP status code (see the related sections below). + +##### Error Details + +Error **details** in an optional field of the JSON error message. Thrown `Exception` should implement the `IHasErrorDetails` interface to fill this field. Example JSON value: + +```json +{ + "error": { + "code": "App:010042", + "message": "This topic is locked and can not add a new message", + "details": "A more detailed info about the error..." + } +} +``` + +##### Validation Errors + +**validationErrors** is a standard field that is filled if the thrown exception implements the `IHasValidationErrors` interface. + +````json +{ + "error": { + "code": "App:010046", + "message": "Your request is not valid, please correct and try again!", + "validationErrors": [{ + "message": "Username should be minimum lenght of 3.", + "members": ["userName"] + }, + { + "message": "Password is required", + "members": ["password"] + }] + } +} +```` + +`AbpValidationException` implements the `IHasValidationErrors` interface and it is automatically thrown by the framework when a request input is not valid. So, usually you don't need to deal with validation errors unless you have higly customised validation logic. + +#### Logging + +Caught exceptions are automatically logged. + +##### Log Level + +Exceptions are logged with the `Error` level by default. The Log level can be determined by the exception if it implements the `IHasLogLevel` interface. Example: + +````C# +public class MyException : Exception, IHasLogLevel +{ + public LogLevel LogLevel { get; set; } = LogLevel.Warning; + + //... +} +```` + +##### Self Logging Exceptions + +Some exception types may need to write additional logs. They can implement the `IExceptionWithSelfLogging` if needed. Example: + +````C# +public class MyException : Exception, IExceptionWithSelfLogging +{ + public void Log(ILogger logger) + { + //...log additional info + } +} +```` + +> `ILogger.LogException` extension methods is used to write exception logs. You can use the same extension method when needed. + +### Business Exceptions + +Most of your own exceptions will be business exceptions. The `IBusinessException` interface is used to mark an exception as a business exception. + +`BusinessException` implements the `IBusinessException` interface in addition to the `IHasErrorCode`, `IHasErrorDetails` and `IHasLogLevel` interfaces. The default log level is `Warning`. + +Usually you have an error code related to a particular business exception. For example: + +````C# +throw new BusinessException(QaErrorCodes.CanNotVoteYourOwnAnswer); +```` + +`QaErrorCodes.CanNotVoteYourOwnAnswer` is just a `const string`. The following error code format is recommended: + +```` +: +```` + +**code-namespace** is a **unique value** specific to your module/application. Example: + +```` +Volo.Qa:010002 +```` + +`Volo.Qa` is the code-namespace here. code-namespace is then will be used while **localizing** exception messages. + +* You can **directly throw** a `BusinessException` or **derive** your own exception types from it when needed. +* All properties are optional for the `BusinessException` class. But you generally set either `ErrorCode` or `Message` property. + +### Exception Localization + +One problem with throwing exceptions is how to localize error messages while sending it to the client. ABP offers two models and their variants. + +#### User Friendly Exception + +If an exception implements the `IUserFriendlyException` interface, then ABP does not change it's `Message` and `Details` properties and directly send it to the client. + +`UserFriendlyException` class is the built-in implementation of the `IUserFriendlyException` interface. Example usage: + +````C# +throw new UserFriendlyException( + "Username should be unique!" +); +```` + +In this way, there is **no need for localization** at all. If you want to localize the message, you can inject and use the standard **string localizer** (see the [localization document](Localization.md)). Example: + +````C# +throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage"]); +```` + +Then define it in the **localization resource** for each language. Example: + +````json +{ + "culture": "en", + "texts": { + "UserNameShouldBeUniqueMessage": "Username should be unique!" + } +} +```` + +String localizer already supports **parameterized messages**. For example: + +````C# +throw new UserFriendlyException(_stringLocalizer["UserNameShouldBeUniqueMessage", "john"]); +```` + +Then the localization text can be: + +````json +"UserNameShouldBeUniqueMessage": "Username should be unique! '{0}' is already taken!" +```` + +* The `IUserFriendlyException` interface is derived from the `IBusinessException` and the `UserFriendlyException` class is derived from the `BusinessException` class. + +#### Using Error Codes + +`UserFriendlyException` is fine, but it has a few problems in advanced usages: + +* It requires you to **inject the string localizer** everywhere and always use it while throwing exceptions. +* However, in some of the cases, it may **not be possible** to inject the string localizer (in a static context or in an entity method). + +Instead of localizing the message while throwing the exception, you can separate the process using **error codes**. + +First, define the **code-namespace** to **localization resource** mapping in the module configuration: + +````C# +services.Configure(options => +{ + options.MapCodeNamespace("Volo.Qa", typeof(QaResource)); +}); +```` + +Then any of the exceptions with `Volo.Qa` namespace will be localized using their given localization resource. The localization resource should always have an entry with the error code key. Example: + +````json +{ + "culture": "en", + "texts": { + "Volo.Qa:010002": "You can not vote your own answer!" + } +} +```` + +Then a business exception can be thrown with the error code: + +````C# +throw new BusinessException(QaDomainErrorCodes.CanNotVoteYourOwnAnswer); +```` + +* Throwing any exception implementing the `IHasErrorCode` interface behaves the same. So, the error code localization approach is not unique to the `BusinessException` class. +* Defining localized string is not required for an error message. If it's not defined, ABP sends the default error message to the client. It does not use the `Message` property of the exception! if you want that, use the `UserFriendlyException` (or use an exception type that implements the `IUserFriendlyException` interface). + +##### Using Message Parameters + +If you have a parameterized error message, then you can set it with the exception's `Data` property. For example: + +````C# +throw new BusinessException("App:010046") +{ + Data = + { + {"UserName", "john"} + } +}; + +```` + +Fortunately there is a shortcut way to code this: + +````C# +throw new BusinessException("App:010046") + .WithData("UserName", "john"); +```` + +Then the localized text can contain the `UserName` parameter: + +````json +{ + "culture": "en", + "texts": { + "App:010046": "Username should be unique. '{UserName}' is already taken!" + } +} +```` + +* `WithData` can be chained with more than one parameter (like `.WithData(...).WithData(...)`). + +### HTTP Status Code Mapping + +ABP tries to automatically determine the most suitable HTTP status code for common exception types by following these rules: + +* For the `AbpAuthorizationException`: + * Returns `401` (unauthorized) if user has not logged in. + * Returns `403` (forbidden) if user has logged in. +* Returns `400` (bad request) for the `AbpValidationException`. +* Returns `404` (not found) for the `EntityNotFoundException`. +* Returns `403` (forbidden) for the `IBusinessException` (and `IUserFriendlyException` since it extends the `IBusinessException`). +* Returns `501` (not implemented) for the `NotImplementedException`. +* Returns `500` (internal server error) for other exceptions (those are assumed as infrastructure exceptions). + +The `IHttpExceptionStatusCodeFinder` is used to automatically determine the HTTP status code. The default implementation is the `DefaultHttpExceptionStatusCodeFinder` class. It can be replaced or extended as needed. + +#### Custom Mappings + +Automatic HTTP status code determination can be overrided by custom mappings. For example: + +````C# +services.Configure(options => +{ + options.Map("Volo.Qa:010002", HttpStatusCode.Conflict); +}); +```` + +### Built-In Exceptions + +Some exception types are automatically thrown by the framework: + +- `AbpAuthorizationException` is thrown if the current user has no permission to perform the requested operation. See authorization document (TODO: link) for more. +- `AbpValidationException` is thrown if the input of the current request is not valid. See validation document (TODO: link) for more. +- `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](Repositories.md). + +You can also throw these type of exceptions in your code (although it's rarely needed). diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md new file mode 100644 index 0000000000..f63040a6e1 --- /dev/null +++ b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md @@ -0,0 +1,157 @@ +## Getting Started ABP With AspNet Core MVC Web Application + +This tutorial explains how to start ABP from scratch with minimal dependencies. You generally want to start with a ***[startup template](https://abp.io/Templates)***. + +### Create A New Project + +1. Create a new empty AspNet Core Web Application from Visual Studio: + +![](images/create-new-aspnet-core-application.png) + +2. Select Empty Template + +![](images/select-empty-web-application.png) + +You could select another template, but I want to show it from a clear project. + +### Install Volo.Abp.AspNetCore.Mvc Package + +Volo.Abp.AspNetCore.Mvc is AspNet Core MVC integration package for ABP. So, install it to your project: + +```` +Install-Package Volo.Abp.AspNetCore.Mvc +```` + +### Create First ABP Module + +ABP is a modular framework and it requires a **startup (root) module** class derived from ``AbpModule``: + +````C# +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.AspNetCore.Modularity; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Modularity; + +namespace BasicAspNetCoreApplication +{ + [DependsOn(typeof(AbpAspNetCoreMvcModule))] + public class AppModule : AbpModule + { + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvcWithDefaultRoute(); + } + } +} +```` + +``AppModule`` is a good name for the startup module for an application. + +ABP packages define module classes and a module can depend on another module. In the code above, our ``AppModule`` depends on ``AbpAspNetCoreMvcModule`` (defined by Volo.Abp.AspNetCore.Mvc package). It's common to add a ``DependsOn`` attribute after installing a new ABP nuget package. + +Instead of Startup class, we are configuring ASP.NET Core pipeline in this module class. + +### The Startup Class + +Next step is to modify Startup class to integrate to ABP module system: + +````C# +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace BasicAspNetCoreApplication +{ + public class Startup + { + public IServiceProvider ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddApplication(); + + return context.Services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app) + { + app.InitializeApplication(); + } + } +} + +```` + +Changed ``ConfigureServices`` method to return ``IServiceProvider`` instead of ``void``. This change allows us to replace AspNet Core's Dependency Injection with another framework (see Autofac integration section below). ``services.AddApplication()`` adds all services defined in all modules beginning from the ``AppModule``. + +``app.InitializeApplication()`` call in ``Configure`` method initializes and starts the application. + +### Hello World! + +The application above does nothing. Let's create an MVC controller does something: + +````C# +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace BasicAspNetCoreApplication.Controllers +{ + public class HomeController : AbpController + { + public IActionResult Index() + { + return Content("Hello World!"); + } + } +} + +```` + +If you run the application, you will see a "Hello World!" message on the page. + +Derived ``HomeController`` from ``AbpController`` instead of standard ``Controller`` class. This is not required, but ``AbpController`` class has useful base properties and methods to make your development easier. + +### Using Autofac as the Dependency Injection Framework + +While AspNet Core's Dependency Injection (DI) system is fine for basic requirements, Autofac provides advanced features like Property Injection and Method Interception which are required by ABP to perform advanced application framework features. + +Replacing AspNet Core's DI system by Autofac and integrating to ABP is pretty easy. + +1. Install Volo.Abp.Autofac package + +```` +Install-Package Volo.Abp.Autofac +```` + +2. Add ``AbpAutofacModule`` Dependency + +````C# +[DependsOn(typeof(AbpAspNetCoreMvcModule))] +[DependsOn(typeof(AbpAutofacModule))] //Add dependency to ABP Autofac module +public class AppModule : AbpModule +{ + ... +} +```` + +3. Change ``services.AddApplication();`` line in the ``Startup`` class as shown below: + +````C# +services.AddApplication(options => +{ + options.UseAutofac(); //Integrate to Autofac +}); +```` + +### Source Code + +Get source code of the sample project created in this tutorial from [here](../samples/BasicAspNetCoreApplication). \ No newline at end of file diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md new file mode 100644 index 0000000000..665cec98b1 --- /dev/null +++ b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md @@ -0,0 +1,71 @@ +## ASP.NET Core MVC Template + +### 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: + +![bookstore-create--template](images/bookstore-create-template.png) + +When you click the *create* button, a new Visual Studio solution is created and downloaded with the name you have provided. + +#### Pre Requirements + +The downloaded project requires; + +* [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/) + +### The Solution Structure + +Extract the zip file downloaded and open in **Visual Studio 2017 (15.7.0+)**: + +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png) + +The solution has a layered structure (based on Domain Driven Design) where; + +* ``.Domain`` is the domain layer. +* ``.Application`` is the application layer. +* ``.Web`` is the presentation layer. +* ``.EntityFrameworkCore`` is the EF Core integration package. + +The solution also contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database. + +### Creating the Database + +Check the **connection string** in the `appsettings.json` file under the `.Web` project: + +````json +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True" + } +} +```` + +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. + +Right click to the `.Web` project and select **Set as StartUp Project**: + +![set-as-startup-project](images/set-as-startup-project.png) + +Open the **Package Manager Console**, select `.EntityFrameworkCore` project as the **Default Project** and run the `Update-Database` command: + +![pcm-update-database](images/pcm-update-database.png) + +This will create a new database based on the configured connection string. + +### Running the Application + +You can now run the application which will open the **home** page: + +![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**. + +![bookstore-user-management](images/bookstore-user-management.png) + +### What's Next? + +* [Application development tutorial](Tutorials/AspNetCore-Mvc/Part-I.md) diff --git a/docs/zh-Hans/Getting-Started-Console-Application.md b/docs/zh-Hans/Getting-Started-Console-Application.md new file mode 100644 index 0000000000..ab7530522b --- /dev/null +++ b/docs/zh-Hans/Getting-Started-Console-Application.md @@ -0,0 +1,124 @@ +## Getting Started ABP With Console Application + +This tutorial explains how to start ABP from scratch with minimal dependencies. You generally want to start with a **[startup template](https://abp.io/Templates)**. + +### Create A New Project + +Create a new Regular .Net Core Console Application from Visual Studio: + +![](images/create-new-net-core-console-application.png) + +### Install Volo.Abp Package + +Volo.Abp.Core is the core nuget package to create ABP based applications. So, install it to your project: + +```` +Install-Package Volo.Abp.Core +```` + +### Create First ABP Module + +ABP is a modular framework and it requires a **startup (root) module** class derived from ``AbpModule``: + +````C# +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace AbpConsoleDemo +{ + public class AppModule : AbpModule + { + + } +} +```` + +``AppModule`` is a good name for the startup module for an application. + +### Initialize The Application + +The next step is to bootstrap the application using the startup module created above: + +````C# +using System; +using Volo.Abp; + +namespace AbpConsoleDemo +{ + class Program + { + static void Main(string[] args) + { + using (var application = AbpApplicationFactory.Create()) + { + application.Initialize(); + + Console.WriteLine("Press ENTER to stop application..."); + Console.ReadLine(); + } + } + } +} + +```` + +``AbpApplicationFactory`` is used to create the application and load all modules taking ``AppModule`` as the startup module. ``Initialize()`` method starts the application. + +### Hello World! + +The application above does nothing. Let's create a service does something: + +````C# +using System; +using Volo.Abp.DependencyInjection; + +namespace AbpConsoleDemo +{ + public class HelloWorldService : ITransientDependency + { + public void SayHello() + { + Console.WriteLine("Hello World!"); + } + } +} + +```` + +``ITransientDependency`` is a special interface of ABP that automatically registers the service as transient (see [dependency injection document](Dependency-Injection.md)). + +Now, we can resolve the ``HelloWorldService`` and say hello. Change the Program.cs as shown below: + +````C# +using System; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; + +namespace AbpConsoleDemo +{ + class Program + { + static void Main(string[] args) + { + using (var application = AbpApplicationFactory.Create()) + { + application.Initialize(); + + //Resolve a service and use it + var helloWorldService = + application.ServiceProvider.GetService(); + helloWorldService.SayHello(); + + Console.WriteLine("Press ENTER to stop application..."); + Console.ReadLine(); + } + } + } +} +```` + +While it's enough for this simple code example, it's always suggested to create scopes in case of directly resolving dependencies from ``IServiceProvider`` (TODO: see DI documentation). + +### Source Code + +Get source code of the sample project created in this tutorial from [here](../samples/BasicConsoleApplication). diff --git a/docs/zh-Hans/Index.md b/docs/zh-Hans/Index.md new file mode 100644 index 0000000000..e5bf05d15b --- /dev/null +++ b/docs/zh-Hans/Index.md @@ -0,0 +1,64 @@ +# ABP 文档 + +## 目录 + +* 入门 + * 从启动模板开始 + * [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) +* 数据访问 + * [Entity Framework Core 集成](Entity-Framework-Core.md) + * [MongoDB 集成](MongoDB.md) +* 测试 diff --git a/docs/zh-Hans/Localization.md b/docs/zh-Hans/Localization.md new file mode 100644 index 0000000000..ba7bf04238 --- /dev/null +++ b/docs/zh-Hans/Localization.md @@ -0,0 +1,191 @@ +## Localization + +ABP's localization system is seamlessly integrated to the `Microsoft.Extensions.Localization` package and compatible with the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization). It adds some useful features and enhancements to make it easier to use in real life application scenarios. + +### Volo.Abp.Localization Package + +> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. + +Volo.Abp.Localization is the core package of the localization system. Install it to your project using the package manager console (PMC): + +``` +Install-Package Volo.Abp.Localization +``` + +Then you can add **AbpLocalizationModule** dependency to your module: + +```c# +using Volo.Abp.Modularity; +using Volo.Abp.Localization; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpLocalizationModule))] + public class MyModule : AbpModule + { + //... + } +} +``` + +#### Creating A Localization Resource + +A localization resource is used to group related localization strings together and separate them from other localization strings of the application. A module generally defines its own localization resource. Localization resource is just a plain class. Example: + +````C# +public class TestResource +{ +} +```` + +Then it should be added using `AbpLocalizationOptions` as shown below: + +````C# +[DependsOn(typeof(AbpLocalizationModule))] +public class MyModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + context.Services.Configure(options => + { + options.Resources + .Add("en") + .AddVirtualJson("/Localization/Resources/Test"); + }); + } +} +```` + +In this example; + +* Added a new localization resource with "en" (English) as the default culture. +* Used JSON files to store the localization strings. +* JSON files are embedded into the assembly using the [virtual file system](Virtual-File-System.md). + +JSON files are located under "/Localization/Resources/Test" project folder as shown below: + +![localization-resource-json-files](images/localization-resource-json-files.png) + +A JSON localization file content is shown below: + +````json +{ + "culture": "en", + "texts": { + "HelloWorld": "Hello World!" + } +} +```` + +* Every localization file should define the `culture` code for the file (like "en" or "en-US"). +* `texts` section just contains key-value collection of the localization strings (keys may have spaces too). + +##### Short Localization Resource Name + +Localization resources are also available in the client (JavaScript) side. So, setting a short name for the localization resource makes it easy to use localization texts. Example: + +````C# +[LocalizationResourceName("Test")] +public class TestResource +{ +} +```` + +See the Getting Localized Test / Client Side section below. + +##### Inherit From Other Resources + +A resource can inherit from other resources which makes possible to re-use existing localization strings without referring the existing resource. Example: + +````C# +[InheritResource(typeof(AbpValidationResource))] +public class TestResource +{ +} +```` + +Alternative inheritance by configuring the `AbpLocalizationOptions`: + +````C# +services.Configure(options => +{ + options.Resources + .Add("en") //Define the resource by "en" default culture + .AddVirtualJson("/Localization/Resources/Test") //Add strings from virtual json files + .AddBaseTypes(typeof(AbpValidationResource)); //Inherit from an existing resource +}); +```` + +* A resource may inherit from multiple resources. +* If the new resource defines the same localized string, it overrides the string. + +##### Extending Existing Resource + +Inheriting from a resource creates a new resource without modifying the existing one. In some cases, you may want to not create a new resource but directly extend an existing resource. Example: + +````C# +services.Configure(options => +{ + options.Resources + .Get() + .AddVirtualJson("/Localization/Resources/Test/Extensions"); +}); +```` + +* If an extension file defines the same localized string, it overrides the string. + +#### Getting Localized Texts + +##### Server Side + +Getting the localized text on the server side is pretty standard. + +###### Simplest Usage In A Class + +````C# +public class MyService +{ + private readonly IStringLocalizer _localizer; + + public MyService(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public void Foo() + { + var str = _localizer["HelloWorld"]; + } +} +```` + +###### Simplest Usage In A Razor View/Page + +````c# +@inject IHtmlLocalizer Localizer + +

@Localizer["HelloWorld"]

+```` + +Refer to the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) for details about using localization on the server side. + +##### Client Side + +ABP provides JavaScript services to use the same localized texts in the client side. + +Get a localization resource: + +````js +var testResource = abp.localization.getResource('Test'); +```` + +Localize a string: + +````js +var str = testResource('HelloWorld'); +```` \ No newline at end of file diff --git a/docs/zh-Hans/Module-Development-Basics.md b/docs/zh-Hans/Module-Development-Basics.md new file mode 100644 index 0000000000..bb193b613e --- /dev/null +++ b/docs/zh-Hans/Module-Development-Basics.md @@ -0,0 +1,128 @@ +## Module Development + +### Introduction + +ABP is itself a modular framework. It also provides an infrastructure and architectural model to develop your own modules. + +### Module Class + +Every module should define a module class. The simplest way of defining a module class is to create a class derived from ``AbpModule`` as shown below: + +````C# +public class BlogModule : AbpModule +{ + +} + +```` + +#### Configuring Dependency Injection & Other Modules + +##### ConfigureServices Method + +``ConfigureServices`` is the main method to add your services to the dependency injection system and configure other modules. Example: + +````C# +public class BlogModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + //... + } +} +```` + +You can register dependencies one by one as stated in Microsoft's [documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection). But ABP has a **conventional dependency registration system** which automatically register all services in your assembly. See the [dependency Injection](Dependency-Injection.md) documentation for more about the dependency injection system. + +You can also configure other services and modules in this way. Example: + +````C# +public class BlogModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + //Configure default connection string for the application + context.Services.Configure(options => + { + options.ConnectionStrings.Default = "......"; + }); + } +} +```` + +See Configuration (TODO: link) document for more about the configuration system. + +##### Pre & Post Configure Services + +``AbpModule`` class also defines ``PreConfigureServices`` and ``PostConfigureServices`` methods to override and write your code just before and just after ``ConfigureServices``. Notice that the code you have written into these methods will be executed before/after the ``ConfigureServices`` methods of all other modules. + +#### Application Initialization + +Once all the services of all modules are configured, the application starts by initializing all modules. In this phase, you can resolve services from ``IServiceProvider`` since it's ready and available. + +##### OnApplicationInitialization Method + +You can override ``OnApplicationInitialization`` method to execute code while application is being started. Example: + +````C# +public class BlogModule : AbpModule +{ + //... + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var myService = context.ServiceProvider.GetService(); + myService.DoSomething(); + } +} +```` + +``OnApplicationInitialization`` is generally used by the startup module to construct the middleware pipeline for ASP.NET Core applications. Example: + +````C# +[DependsOn(typeof(AbpAspNetCoreMvcModule))] +public class AppModule : AbpModule +{ + //... + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvcWithDefaultRoute(); + } +} +```` + +You can also perform startup logic if your module requires it + +##### Pre & Post Application Initialization + +``AbpModule`` class also defines ``OnPreApplicationInitialization`` and ``OnPostApplicationInitialization`` methods to override and write your code just before and just after ``OnApplicationInitialization``. Notice that the code you have written into these methods will be executed before/after the ``OnApplicationInitialization`` methods of all other modules. + +#### Application Shutdown + +Lastly, you can override ``OnApplicationShutdown`` method if you want to execute some code while application is beign shutdown. + +### 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: + +````C# +[DependsOn(typeof(AbpAspNetCoreMvcModule))] +[DependsOn(typeof(AbpAutofacModule))] +public class BlogModule +{ + //... +} +```` + +You can use multiple ``DependsOn`` attribute or pass multiple module types to a single ``DependsOn`` attribute depending on your preference. + +A depended module may depend on another module, but you only need to define your direct dependencies. ABP investigates the dependency graph for the application at startup and initializes/shutdowns modules in the correct order. diff --git a/docs/zh-Hans/MongoDB.md b/docs/zh-Hans/MongoDB.md new file mode 100644 index 0000000000..591c4d5834 --- /dev/null +++ b/docs/zh-Hans/MongoDB.md @@ -0,0 +1,99 @@ +## MongoDB Integration + +This document explains how to integrate MongoDB as a database provider to ABP based applications and how to configure it. + +### Installation + +`Volo.Abp.MongoDB` is the main nuget package for the MongoDB integration. Install it to your project (for a layered application, to your data/infrastructure layer): + +``` +Install-Package Volo.Abp.MongoDB +``` + +Then add `AbpMongoDbModule` module dependency to your [module](Module-Development-Basics.md): + +```c# +using Volo.Abp.MongoDB; +using Volo.Abp.Modularity; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMongoDbModule))] + public class MyModule : AbpModule + { + //... + } +} +``` + +### Creating a Mongo Db Context + +ABP introduces **Mongo Db Context** concept (which is similar to Entity Framework Core's DbContext) to make it easier to use collections and configure them. An example is shown below: + +```c# +public class MyDbContext : AbpMongoDbContext +{ + public IMongoCollection Questions => Collection(); + + public IMongoCollection Categories => Collection(); + + protected override void CreateModel(IMongoModelBuilder modelBuilder) + { + modelBuilder.Entity(b => + { + b.CollectionName = "Questions"; + }); + } +} +``` + +* It's derived from `AbpMongoDbContext` class. +* Adds a public `IMongoCollection` property for each mongo collection. ABP uses these properties to create default repositories by default. +* Overriding `CreateModel` method allows to configure collections (like their collection name in the database). + +### Registering DbContext To Dependency Injection + +Use `AddAbpDbContext` method in your module to register your DbContext class for [dependency injection](Dependency-Injection.md) system. + +```c# +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.MongoDB; +using Volo.Abp.Modularity; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMongoDbModule))] + public class MyModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMongoDbContext(); + + //... + } + } +} +``` + +#### Add Default Repositories + +ABP can automatically create [repositories](Repositories.md) for the entities in your Db Context. Just use `AddDefaultRepositories()` option on registration: + +````C# +services.AddMongoDbContext(options => +{ + options.AddDefaultRepositories(); +}); +```` + +This will create a repository for each aggreate root entity (classes derived from AggregateRoot) by default. If you want to create repositories for other entities too, then set `includeAllEntities` to `true`: + +```c# +services.AddMongoDbContext(options => +{ + options.AddDefaultRepositories(includeAllEntities: true); +}); +``` + +Then you can inject and use `IRepository` or `IQueryableRepository` in your services. + diff --git a/docs/zh-Hans/Multi-Tenancy.md b/docs/zh-Hans/Multi-Tenancy.md new file mode 100644 index 0000000000..6697e10124 --- /dev/null +++ b/docs/zh-Hans/Multi-Tenancy.md @@ -0,0 +1,389 @@ +## Multi-Tenancy + +ABP Multi-tenancy module provides base functionality to create multi tenant applications. + +Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) multi-tenancy as like that: + +> Software **Multi-tenancy** refers to a software **architecture** in which a **single instance** of a software runs on a server and serves **multiple tenants**. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a **dedicated share of the instance including its data**, configuration, user management, tenant individual functionality and non-functional properties. Multi-tenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants. + +### Volo.Abp.MultiTenancy.Abstractions Package + +Volo.Abp.MultiTenancy.Abstractions package defines fundamental interfaces to make your code "multi-tenancy ready". So, install it to your project using the package manager console (PMC): + +```` +Install-Package Volo.Abp.MultiTenancy.Abstractions +```` + +> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. + +Then you can add **AbpMultiTenancyAbstractionsModule** dependency to your module: + +````C# +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] + public class MyModule : AbpModule + { + //... + } +} +```` + +> With the "Multi-tenancy ready" concept, we intent to develop our code to be compatible with multi-tenancy approach. Then it can be used in a multi-tenant application or not, depending on the requirements of the final application. + +#### Define Entities + +You can implement **IMultiTenant** interface for your entities to make them multi-tenancy ready. Example: + +````C# +using System; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + public class Product : AggregateRoot, IMultiTenant + { + public Guid? TenantId { get; set; } //IMultiTenant defines TenantId property + + public string Name { get; set; } + + public float Price { get; set; } + } +} +```` + +IMultiTenant requires to define a **TenantId** property in the implementing entity (See entity documentation (TODO: link) for more about entities). + +#### Obtain Current Tenant's Id + +Your code may require to get current tenant's id (regardless of how it's retrieved actually). You can [inject](Dependency-Injection.md) and use **ICurrentTenant** interface for such cases. Example: + +````C# +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + public class MyService : ITransientDependency + { + private readonly ICurrentTenant _currentTenant; + + public MyService(ICurrentTenant currentTenant) + { + _currentTenant = currentTenant; + } + + public void DoIt() + { + var tenantId = _currentTenant.Id; + //use tenantId in your code... + } + } +} +```` + +#### Change Current Tenant + +TODO: ... + +### Volo.Abp.MultiTenancy Package + +Volo.Abp.MultiTenancy is the actual package that makes your application multi-tenant. Install it into your project using PMC: + +```` +Install-Package Volo.Abp.MultiTenancy +```` + +Then you can add **AbpMultiTenancyAbstractionsModule** dependency to your module: + +````C# +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMultiTenancyModule))] + public class MyModule : AbpModule + { + //... + } +} +```` + +> If you add AbpMultiTenancyModule dependency to your module, then you don't need to add AbpMultiTenancyAbstractionsModule dependency separately since AbpMultiTenancyModule already depends on it. + +#### Determining Current Tenant + +The first thing for a multi-tenant application is to determine the current tenant on the runtime. Volo.Abp.MultiTenancy package only provides abstractions (named as tenant resolver) for determining the current tenant, however it does not have any implementation out of the box. + +**Volo.Abp.AspNetCore.MultiTenancy** package has implementation to determine the current tenant from current web request (from subdomain, header, cookie, route... etc.). See Volo.Abp.AspNetCore.MultiTenancy Package section later in this document. + +##### Custom Tenant Resolvers + +You can add your custom tenant resolver to **TenantResolveOptions** in your module's ConfigureServices method as like below: + +````C# +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMultiTenancyModule))] + public class MyModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + options.TenantResolvers.Add(new MyCustomTenantResolver()); + }); + + //... + } + } +} +```` + +MyCustomTenantResolver must implement **ITenantResolver** as shown below: + +````C# +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + public class MyCustomTenantResolver : ITenantResolver + { + public void Resolve(ITenantResolveContext context) + { + context.TenantIdOrName = ... //find tenant id or tenant name from somewhere... + } + } +} +```` + +A tenant resolver can set **TenantIdOrName** if it can determine it. If not, just leave it as is to allow next resolver to determine it. + +#### Tenant Store + +Volo.Abp.MultiTenancy package defines **ITenantStore** to abstract data source from the framework. You can implement ITenantStore to work with any data source (like a relational database) that stores information of your tenants. + +... + +##### Configuration Data Store + +There is a built in (and default) tenant store, named ConfigurationTenantStore, that can be used to store tenants using standard [configuration system](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/) (with [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration) package). Thus, you can define tenants as hard coded or get from your appsettings.json file. + +###### Example: Define tenants as hard-coded + +````C# +using System; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Data; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMultiTenancyModule))] + public class MyModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + options.Tenants = new[] + { + new TenantInformation( + Guid.Parse("446a5211-3d72-4339-9adc-845151f8ada0"), //Id + "tenant1" //Name + ), + new TenantInformation( + Guid.Parse("25388015-ef1c-4355-9c18-f6b6ddbaf89d"), //Id + "tenant2" //Name + ) + { + //tenant2 has a seperated database + ConnectionStrings = + { + {ConnectionStrings.DefaultConnectionStringName, "..."} + } + } + }; + }); + } + } +} +```` + +###### Example: Define tenants in appsettings.json + +First create your configuration from your appsettings.json file as you always do. + +````C# +using System.IO; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpMultiTenancyModule))] + public class MyModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = BuildConfiguration(); + + context.Services.Configure(configuration); + } + + private static IConfigurationRoot BuildConfiguration() + { + return new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .Build(); + } + } +} +```` + +Then add a "**Tenants**" section to your appsettings.json: + +````json +"Tenants": [ + { + "Id": "446a5211-3d72-4339-9adc-845151f8ada0", + "Name": "tenant1" + }, + { + "Id": "25388015-ef1c-4355-9c18-f6b6ddbaf89d", + "Name": "tenant2", + "ConnectionStrings": { + "Default": "...write tenant2's db connection string here..." + } + } + ] +```` + +##### Volo.Abp... Package (TODO) + +TODO: This package implements ITenantStore using a real database... + +#### Tenant Information + +ITenantStore works with **TenantInformation** class that has several properties for a tenant: + +* **Id**: Unique Id of the tenant. +* **Name**: Unique name of the tenant. +* **ConnectionStrings**: If this tenant has dedicated database(s) to store it's data, then connection strings can provide database connection strings (it may have a default connection string and connection strings per modules - TODO: Add link to Abp.Data package document). + +A multi-tenant application may require additional tenant properties, but these are the minimal requirements for the framework to work with multiple tenants. + +#### Change Tenant By Code + +TODO... + +### Volo.Abp.AspNetCore.MultiTenancy Package + +Volo.Abp.AspNetCore.MultiTenancy package integrate multi-tenancy to ASP.NET Core applications. To install it to your project, run the following command on PMC: + +```` +Install-Package Volo.Abp.AspNetCore.MultiTenancy +```` + +Then you can add **AbpAspNetCoreMultiTenancyModule** dependency to your module: + +````C# +using Volo.Abp.Modularity; +using Volo.Abp.AspNetCore.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpAspNetCoreMultiTenancyModule))] + public class MyModule : AbpModule + { + //... + } +} +```` + +#### Multi-Tenancy Middleware + +Volo.Abp.AspNetCore.MultiTenancy package includes the multi-tenancy middleware... + +````C# +app.UseMultiTenancy(); +```` + +TODO:... + +#### Determining Current Tenant From Web Request + +Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box: + +* **QueryStringTenantResolver**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default. +* **RouteTenantResolver**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route. +* **HeaderTenantResolver**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default. +* **CookieTenantResolver**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default. + +"__tenant" parameter name can be changed using AspNetCoreMultiTenancyOptions. Example: + +````C# +services.Configure(options => +{ + options.TenantKey = "MyTenantKey"; +}); +```` + +##### Domain Tenant Resolver + +In a real application, most of times you will want to determine current tenant either by subdomain (like mytenant1.mydomain.com) or by the whole domain (like mytenant.com). If so, you can configure TenantResolveOptions to add a domain tenant resolver. + +###### Example: Add a subdomain resolver + +````C# +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpAspNetCoreMultiTenancyModule))] + public class MyModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.Configure(options => + { + //Subdomain format: {0}.mydomain.com (adding as the highest priority resolver) + options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com")); + }); + + //... + } + } +} +```` + +{0} is the the placeholder to determine current tenant's unique name. + +Instead of ``options.TenantResolvers.Insert(0, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut: + +````C# +options.AddDomainTenantResolver("{0}.mydomain.com"); +```` + +###### Example: Add a domain resolver + +````C# +options.AddDomainTenantResolver("{0}.com"); +```` + diff --git a/docs/zh-Hans/Object-To-Object-Mapping.md b/docs/zh-Hans/Object-To-Object-Mapping.md new file mode 100644 index 0000000000..9bb22e0c66 --- /dev/null +++ b/docs/zh-Hans/Object-To-Object-Mapping.md @@ -0,0 +1,3 @@ +## Object To Object Mapping + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Repositories.md b/docs/zh-Hans/Repositories.md new file mode 100644 index 0000000000..228d066a0e --- /dev/null +++ b/docs/zh-Hans/Repositories.md @@ -0,0 +1,117 @@ +## Repositories + +"*Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects*" (Martin Fowler). + +Repositories, in practice, are used to perform database operations for domain objects (see [Entities](Entities.md)). Generally, a separated repository is used for each **aggregate root** or entity. + +### Generic Repositories + +ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository` into your service and perform standard **CRUD** operations. Example usage: + +````C# +public class PersonAppService : ApplicationService +{ + private readonly IRepository _personRepository; + + public PersonAppService(IRepository personRepository) + { + _personRepository = personRepository; + } + + public async Task Create(CreatePersonDto input) + { + var person = new Person { Name = input.Name, Age = input.Age }; + + await _personRepository.InsertAsync(person); + } + + public List GetList(string nameFilter) + { + var people = _personRepository + .Where(p => p.Name.Contains(nameFilter)) + .ToList(); + + return people + .Select(p => new PersonDto {Id = p.Id, Name = p.Name, Age = p.Age}) + .ToList(); + } +} +```` + +In this example; + +* `PersonAppService` simply injects `IRepository` in it's constructor. +* `Create` method uses `InsertAsync` to save a newly created entity. +* `GetList` method uses the standard LINQ `Where` and `ToList` methods to filter and get a list of people from the data source. + +> The example above uses hand-made mapping between [entities](Entities.md) and [DTO](Data-Transfer-Objects.md)s. See [object to object mapping document](Object-To-Object-Mapping.md) for an automatic way of mapping. + +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 `Delete` method to delete multiple entities by a filter. +* Implements `IQueryable`, so you can use LINQ and extension methods like `FirstOrDefault`, `Where`, `OrderBy`, `ToList` and so on... +* Have **sync** and **async** versions for all methods. + +#### Generic Repository without a Primary Key + +If your entity does not have an Id primary key (it may have a composite primary key for instance) then you cannot use the `IRepository` defined above. In that case, you can inject and use `IRepository` for your entity. + +> `IRepository` has a few missing methods those normally works with the `Id` property of an entity. Because of the entity has no `Id` property in that case, these methods are not available. One example is the `Get` method that gets an id and returns the entity with given id. However, you can still use `IQueryable` features to query entities by standard LINQ methods. + +### Basic Repositories + +Standard `IRepository` interface extends standard `IQueryable` and you can freely query using standard LINQ methods. However, some ORM providers or database systems may not support standard `IQueryable` interface. + +ABP provides `IBasicRepository` and `IBasicRepository` interfaces to support such scenarios. You can extend these interfaces (and optionally derive from `BasicRepositoryBase`) to create custom repositories for your entities. + +Depending on `IBasicRepository` but not depending on `IRepository` has an advantage to make possible to work with all data sources even if they don't support `IQueryable`. But major vendors, like Entity Framework, NHibernate or MongoDb already support `IQueryable`. + +So, working with `IRepository` is the **suggested** way for typical applications. But reusable module developers may consider `IBasicRepository` to support a wider range of data sources. + +### Custom Repositories + +Default generic repositories will be sufficient for most cases. However, you may need to create a custom repository class for your entity. + +#### Custom Repository Example + +ABP does not force you to implement any interface or inherit from any base class for a repository. It can be just a simple POCO class. However, it's suggested to inherit existing repository interface and classes to make your work easier and get the standard methods out of the box. + +##### Custom Repository Interface + +First, define an interface in your domain layer: + +```c# +public interface IPersonRepository : IRepository +{ + Task FindByNameAsync(string name); +} +``` + +This interface extends `IRepository` to take advantage of pre-built repository functionality. + +##### Custom Repository Implementation + +A custom repository is tightly coupled to the data access tool type you are using. In this example, we will use Entity Framework Core: + +````C# +public class PersonRepository : EfCoreRepository, IPersonRepository +{ + public PersonRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public async Task FindByNameAsync(string name) + { + return await DbContext.Set() + .Where(p => p.Name == name) + .FirstOrDefaultAsync(); + } +} +```` + +You can directly access the data access provider (`DbContext` in this case) to perform operations. See [entity framework integration document](Entity-Framework-Core.md) for more about custom repositories based on EF Core. + diff --git a/docs/zh-Hans/Tag-Helpers.md b/docs/zh-Hans/Tag-Helpers.md new file mode 100644 index 0000000000..2d8b465c23 --- /dev/null +++ b/docs/zh-Hans/Tag-Helpers.md @@ -0,0 +1,3 @@ +## ABP Tag Helpers + +"ABP tag helpers" is not documented yet. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now. \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md new file mode 100644 index 0000000000..5ed244a53c --- /dev/null +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md @@ -0,0 +1,426 @@ +## ASP.NET Core MVC Tutorial - Part I + +### 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)). + +This is the first part of the 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). + +### 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). + +### Solution Structure + +This is the how the layered solution structure looks after it's created from the startup template: + +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png) + +### 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`: + +````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 + { + [Required] + [StringLength(128)] + public string Name { get; set; } + + public BookType Type { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + } +} +```` + +* 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. +* `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: + +````C# +namespace Acme.BookStore +{ + public enum BookType : byte + { + Undefined, + Advanture, + Biography, + Dystopia, + Fantastic, + Horror, + Science, + ScienceFiction, + Poetry + } +} +```` + +#### Add Book Entity to Your DbContext + +EF Core requires you to relate entities with your DbContext. The easiest way to do this is to add a `DbSet` property to the `BookStoreDbContext` class in the `Acme.BookStore.EntityFrameworkCore` project, as shown below: + +````C# +public class BookStoreDbContext : AbpDbContext +{ + public DbSet Book { get; set; } + ... +} +```` + +#### Add New Migration & Update the Database + +The Startup template uses [EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) to create and maintain the database schema. Open the **Package Manager Console (PMC)** (under the *Tools/Nuget Package Manager* menu), select the `Acme.BookStore.EntityFrameworkCore` as the **default project** and execute the following command: + +![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration.png) + +This will create a new migration class inside the `Migrations` folder. Then execute the `Update-Database` command to update the database schema: + +```` +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: + +![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. + +#### BookDto + +Create a DTO class named `BookDto` into the `Acme.BookStore.Application` project: + +````C# +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AutoMapper; + +namespace Acme.BookStore +{ + [AutoMapFrom(typeof(Book))] + public class BookDto : AuditedEntityDto + { + public string Name { get; set; } + + public BookType Type { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + } +} +```` + +* **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` 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). + +#### CreateUpdateBookDto + +Create a DTO class named `CreateUpdateBookDto` into the `Acme.BookStore.Application` project: + +````c# +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.AutoMapper; + +namespace Acme.BookStore +{ + [AutoMapTo(typeof(Book))] + public class CreateUpdateBookDto + { + [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; } + } +} +```` + +* 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. + +#### IBookAppService + +Define an interface named `IBookAppService` for the book application service: + +````C# +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Acme.BookStore +{ + public interface IBookAppService : + IAsyncCrudAppService< //Defines CRUD methods + BookDto, //Used to show books + Guid, //Primary key of the book entity + PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books + CreateUpdateBookDto, //Used to create a new book + CreateUpdateBookDto> //Used to update a book + { + + } +} +```` + +* Defining interfaces for application services is not required 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. + +#### BookAppService + +Implement the `IBookAppService` as named `BookAppService`: + +````C# +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace Acme.BookStore +{ + public class BookAppService : + AsyncCrudAppService, + IBookAppService + { + public BookAppService(IRepository repository) + : base(repository) + { + + } + } +} +```` + +* `BookAppService` is derived from `AsyncCrudAppService<...>` which implements all the CRUD methods defined above. +* `BookAppService` injects `IRepository` 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. + +### 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** 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. + +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) + +### 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. + +ABP **dynamically** creates JavaScript **proxies** for all API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**. + +#### 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: + +````js +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. +* `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: + +![bookstore-test-js-proxy-getlist](images/bookstore-test-js-proxy-getlist.png) + +You can see the **book list** returned from the server. You can also check the **network** tab of the developer tools to see the client to server communication: + +![bookstore-test-js-proxy-getlist-network](images/bookstore-test-js-proxy-getlist-network.png) + +Let's **create a new book** using the `create` function: + +````js +acme.bookStore.book.create({ name: 'Foundation', type: 7, publishDate: '1951-05-24', price: 21.5 }).done(function (result) { console.log('successfully created the book with id: ' + result.id); }); +```` + +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. + +### Create the Books Page + +It's time to create something visible and usable! Instead of classic MVC, we will use the new [Razor Pages UI](https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/razor-pages-start) approach which is recommended by Microsoft. + +Create a new `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project and add a new Razor Page named `Index.html`: + +![bookstore-add-index-page](images/bookstore-add-index-page.png) + +Open the `Index.cshtml` and change the content as shown below: + +````html +@page +@using Acme.BookStore.Pages.Books +@inherits Acme.BookStore.Pages.BookStorePageBase +@model IndexModel + +

Books

+```` + +* 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. + +#### Add Books Page to the Main Menu + +Open the `BookStoreMenuContributor` class in the `Menus` folder and add the following code to the end of the `ConfigureMainMenuAsync` method: + +````c# +context.Menu.AddItem( + new ApplicationMenuItem("BooksStore", l["Menu:BookStore"]) + .AddItem(new ApplicationMenuItem("BooksStore.Books", l["Menu:Books"], url: "/Books")) +); +```` + +#### Localizing the Menu Items + +Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain` project: + +![bookstore-localization-files](images/bookstore-localization-files.png) + +Open the `en.json` file and add localization texts for `Menu:BookStore` and `Menu:Books` keys: + +````json +{ + "culture": "en", + "texts": { + //... + "Menu:BookStore": "Book Store", + "Menu:Books": "Books" + } +} +```` + +* 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). + +Run the application and see the menu items are added to the top bar: + +![bookstore-menu-items](images/bookstore-menu-items.png) + +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. + +##### Index.cshtml Changes + +Change the `Pages/Books/Index.cshtml` as following: + +````html +@page +@using Acme.BookStore.Pages.Books +@inherits Acme.BookStore.Pages.BookStorePageBase +@model IndexModel +@section scripts +{ + +} + + +

@L["Books"]

+
+ + + + + @L["Name"] + @L["Type"] + @L["PublishDate"] + @L["Price"] + @L["CreationTime"] + + + + +
+```` + +* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to standard `script` tag. It handles **minification** and **versioning** for example. See the [bundling & minification document](../../AspNetCore/Bundling-Minification.md) for details. +* `abp-card` and `abp-table` are **tag helpers** for Twitter Bootstrap's [card component](http://getbootstrap.com/docs/4.1/components/card/). There are many tag helpers in ABP to easily use most of the [bootstrap](https://getbootstrap.com/) components. You can also use regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by help of the intellisense and compile time type checking. See the [tag helpers document](../../AspNetCore/Tag-Helpers.md). +* You can **localize** the column names in the localization file as you did for the menu items above. + +##### Add a Script File + +Create `index.js` JavaScript file under the `Pages/Books/` folder: + +![bookstore-index-js-file](images/bookstore-index-js-file.png) + +`index.js` content is shown below: + +````js +$(function () { + var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { data: "name" }, + { data: "type" }, + { data: "publishDate" }, + { data: "price" }, + { data: "creationTime" } + ] + })); +}); +```` + +* `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. +* `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. + +The final UI is shown below: + +![bookstore-book-list](images/bookstore-book-list.png) + +### Next Part + +See the [next part](Part-II.md) of this tutorial. diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md new file mode 100644 index 0000000000..e35c4d5243 --- /dev/null +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md @@ -0,0 +1,421 @@ +## ASP.NET Core MVC Tutorial - Part II + +### About this Tutorial + +This is the second part of the 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). + +### Creating a New Book + +In this section, you will learn how to create a new modal dialog form to create a new book. The result dialog will be like that: + +![bookstore-create-dialog](images/bookstore-create-dialog.png) + +#### Create the Modal Form + +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) + +##### CreateModal.cshtml.cs + +Open the `CreateModal.cshtml.cs` file (`CreateModalModel` class) and replace with the following code: + +````C# +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Acme.BookStore.Pages.Books +{ + public class CreateModalModel : BookStorePageModelBase + { + [BindProperty] + public CreateUpdateBookDto Book { get; set; } + + private readonly IBookAppService _bookAppService; + + public CreateModalModel(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnPostAsync() + { + await _bookAppService.CreateAsync(Book); + return NoContent(); + } + } +} +```` + +* This class is derived from the `BookStorePageModelBase` instead of standard `PageModel`. `BookStorePageModelBase` inherits the `PageModel` and adds some common properties/methods those can be used by your page model classes. +* `[BindProperty]` attribute on the `Book` property binds post request data to this property. +* This class simply injects the `IBookAppService` in its constructor and calls the `CreateAsync` method in the `OnPostAsync` handler. + +##### CreateModal.cshtml + +Open the `CreateModal.cshtml` file and paste the code below: + +````html +@page +@inherits Acme.BookStore.Pages.BookStorePageBase +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model Acme.BookStore.Pages.Books.CreateModalModel +@{ + Layout = null; +} + + + + + + + + + +```` + +* 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). + +#### Add the "New book" Button + +Open the `Pages/Books/Index.cshtml` and change the `abp-card-header` tag as shown below: + +````html + + + +

@L["Books"]

+
+ + + +
+
+```` + +Just added a **New book** button to the **top right** of the table: + +![bookstore-new-book-button](images/bookstore-new-book-button.png) + +Open the `wwwroot/pages/books/index.js` and add the following code just after the datatable configuration: + +````js +var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + +createModal.onResult(function () { + dataTable.ajax.reload(); +}); + +$('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); +}); +```` + +* `abp.ModalManager` is a helper class to open and manage modals in the client side. It internally uses Twitter Bootstrap's standard modal, but abstracts many details by providing a simple API. + +Now, you can **run the application** and add new books using the new modal form. + +### Updating An Existing Book + +Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project: + +![bookstore-add-edit-dialog](images/bookstore-add-edit-dialog.png) + +#### EditModal.cshtml.cs + +Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code: + +````C# +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Acme.BookStore.Pages.Books +{ + public class EditModalModel : BookStorePageModelBase + { + [HiddenInput] + [BindProperty(SupportsGet = true)] + public Guid Id { get; set; } + + [BindProperty] + public CreateUpdateBookDto Book { get; set; } + + private readonly IBookAppService _bookAppService; + + public EditModalModel(IBookAppService bookAppService) + { + _bookAppService = bookAppService; + } + + public async Task OnGetAsync() + { + var bookDto = await _bookAppService.GetAsync(Id); + Book = ObjectMapper.Map(bookDto); + } + + public async Task OnPostAsync() + { + await _bookAppService.UpdateAsync(Id, Book); + return NoContent(); + } + } +} +```` + +* `[HiddenInput]` and `[BindProperty]` are standard ASP.NET Core MVC attributes. Used `SupportsGet` to be able to get Id value from query string parameter of the request. +* Mapped `BookDto` (received from the `BookAppService.GetAsync`) to `CreateUpdateBookDto` in the `GetAsync` method. +* The `OnPostAsync` simply uses `BookAppService.UpdateAsync` to update the entity. + +#### CreateUpdateBookDto + +In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, change the `CreateUpdateBookDto` class as shown below: + +````C# +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.AutoMapper; + +namespace Acme.BookStore +{ + [AutoMapTo(typeof(Book))] + [AutoMapFrom(typeof(BookDto))] + public class CreateUpdateBookDto + { + [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; } + } +} +```` + +* Just added the `[AutoMapFrom(typeof(BookDto))]` attribute to create the mapping. + +#### EditModal.cshtml + +Replace `EditModal.cshtml` content with the following content: + +````html +@page +@inherits Acme.BookStore.Pages.BookStorePageBase +@using Acme.BookStore.Pages.Books +@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal +@model EditModalModel +@{ + Layout = null; +} + + + + + + + + + + +```` + +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 uses `Books/EditModal` as the post URL and *Update* text as the modal header. + +#### Add "Actions" Dropdown to the Table + +We will add a dropdown button ("Actions") for each row of the table. The final UI looks like this: + +![bookstore-books-table-actions](images/bookstore-books-table-actions.png) + +Open the `Pages/Books/Index.cshtml` page and change the table section as shown below: + +````html + + + + @L["Actions"] + @L["Name"] + @L["Type"] + @L["PublishDate"] + @L["Price"] + @L["CreationTime"] + + + +```` + +* Just added a new `th` tag for the "Actions". + +Open the `wwwroot/pages/books/index.js` and replace the content as below: + +````js +$(function () { + + var l = abp.localization.getResource('BookStore'); + + var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); + + var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + order: [[1, "asc"]], + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { + rowAction: { + items: + [ + { + text: l('Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + } + ] + } + }, + { data: "name" }, + { data: "type" }, + { data: "publishDate" }, + { data: "price" }, + { data: "creationTime" } + ] + })); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +* Used `abp.localization.getResource('BookStore')` to be able to use the same localization texts defined on the server side. +* Added a new `ModalManager` named `editModal` to open the edit modal dialog. +* Added a new column at the beginning of the `columnDefs` section. This column is used for the "Actions" dropdown button. +* "Edit" action simply calls `editModal.open` to open the edit dialog. + +You can run the application and edit any book by selecting the edit action. + +### Deleting an Existing Book + +Open the `wwwroot/pages/books/index.js` and add a new item to the `rowAction` `items`: + +````js +{ + text: l('Delete'), + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function() { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } +} +```` + +* `confirmMessage` option is used to ask a confirmation question before executing the `action`. +* Used `acme.bookStore.book.delete` javascript proxy function to perform an AJAX request to delete a book. +* `abp.notify.info` is used to show a toastr notification just after the deletion. + +The final `index.js` content is shown below: + +````js +$(function () { + + var l = abp.localization.getResource('BookStore'); + + var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); + var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); + + var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ + order: [[1, "asc"]], + ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), + columnDefs: [ + { + rowAction: { + items: + [ + { + text: l('Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } + }, + { + text: l('Delete'), + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function() { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { data: "name" }, + { data: "type" }, + { data: "publishDate" }, + { data: "price" }, + { data: "creationTime" } + ] + })); + + createModal.onResult(function () { + dataTable.ajax.reload(); + }); + + editModal.onResult(function () { + dataTable.ajax.reload(); + }); + + $('#NewBookButton').click(function (e) { + e.preventDefault(); + createModal.open(); + }); +}); +```` + +Run the application and try to delete a book. + +### Next Part + +See the [next part](Part-III.md) of this tutorial. \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md new file mode 100644 index 0000000000..a45d406ce6 --- /dev/null +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md @@ -0,0 +1,217 @@ +## ASP.NET Core MVC Tutorial - Part III + +### About this Tutorial + +This is the third part of the 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). + +### Test Projects in the Solution + +There are two test projects in the solution: + +![bookstore-test-projects](images/bookstore-test-projects.png) + +* `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. + +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. +* [NSubstitute](http://nsubstitute.github.io/) as a mocking library. + +### 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: + +````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: + +````C# +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; +using Volo.Abp.Threading; + +namespace Acme.BookStore +{ + public class BookStoreTestDataBuilder : ITransientDependency + { + private readonly IIdentityDataSeeder _identityDataSeeder; + private readonly IRepository _bookRepository; + + public BookStoreTestDataBuilder( + IIdentityDataSeeder identityDataSeeder, + IRepository bookRepository) + { + _identityDataSeeder = identityDataSeeder; + _bookRepository = bookRepository; + } + + public void Build() + { + AsyncHelper.RunSync(BuildInternalAsync); + } + + public async Task BuildInternalAsync() + { + await _identityDataSeeder.SeedAsync("1q2w3E*"); + + await _bookRepository.InsertAsync( + new Book + { + Id = Guid.NewGuid(), + Name = "Test book 1", + Type = BookType.Fantastic, + PublishDate = new DateTime(2015, 05, 24), + Price = 21 + } + ); + + await _bookRepository.InsertAsync( + new Book + { + Id = Guid.NewGuid(), + Name = "Test book 2", + Type = BookType.Science, + PublishDate = new DateTime(2014, 02, 11), + Price = 15 + } + ); + } + } +} +```` + +* Injected `IRepository` and used it in the `BuildInternalAsync` to create two book entities. + +### Testing the BookAppService + +Create a test class named `BookAppService_Tests` in the `Acme.BookStore.Application.Tests` project: + +````C# +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Application.Dtos; +using Xunit; + +namespace Acme.BookStore +{ + public class BookAppService_Tests : BookStoreApplicationTestBase + { + private readonly IBookAppService _bookAppService; + + public BookAppService_Tests() + { + _bookAppService = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_List_Of_Books() + { + //Act + var result = await _bookAppService.GetListAsync( + new PagedAndSortedResultRequestDto() + ); + + //Assert + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(b => b.Name == "Test book 1"); + } + } +} +```` + +* `Should_Get_List_Of_Books` test simply uses `BookAppService.GetListAsync` method to get and check the list of users. + +Add a new test that creates a valid new book: + +````C# +[Fact] +public async Task Should_Create_A_Valid_Book() +{ + //Act + var result = await _bookAppService.CreateAsync( + new CreateUpdateBookDto + { + Name = "New test book 42", + Price = 10, + PublishDate = DateTime.Now, + Type = BookType.ScienceFiction + } + ); + + //Assert + result.Id.ShouldNotBe(Guid.Empty); + result.Name.ShouldBe("New test book 42"); +} +```` + +Add a new test that tries to create an invalid book and fails: + +````C# +[Fact] +public async Task Should_Not_Create_A_Book_Without_Name() +{ + var exception = await Assert.ThrowsAsync(async () => + { + await _bookAppService.CreateAsync( + new CreateUpdateBookDto + { + Name = "", + Price = 10, + PublishDate = DateTime.Now, + Type = BookType.ScienceFiction + } + ); + }); + + exception.ValidationErrors + .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name")); +} +```` + +* Since the `Name` is empty, ABP throws an `AbpValidationException`. + +### Testing Web Pages + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..44398e6b4c4fa0f83881b2e63f29bed6f32f9a6d GIT binary patch literal 8437 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 literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-edit-dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..b28d8736dd19efa21f65e6e5a9c8d4b82a613f4c GIT binary patch literal 4006 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 M07*qoM6N<$g0meFxBvhE literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png new file mode 100644 index 0000000000000000000000000000000000000000..2452ca546eb7c3991102cf1769a0aa81ca5ef221 GIT binary patch literal 8699 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 literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-books-table-actions.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1d4e3f6f06b09879bff2ecdedbd0ddd56f0d0b GIT binary patch literal 9339 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 literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-create-template.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc96c8c943ede472f900a0b0f73be8874b5e4c2 GIT binary patch literal 32422 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 literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png new file mode 100644 index 0000000000000000000000000000000000000000..e36c5dfff74bc68292fcdb61cd4123db3e566af0 GIT binary patch literal 3592 zcmYLM2UHWw`(1*7)Sz?_h|)m`MV=1>(pzXrAkri>l_o`cQ9!x~!h?X)L&t=s2ug1v zD!n(65{h&}3{~+L-#h>F-*aYX&fVSn?cDjkJ7=CEO!Qcp_?Z9zU^UR!Mp647YV$r1 zqTa>I$#f3GODc+0Dw z6m7JrRS->s|nJCr;+4Xxsw zAeIcjcka>xAYjIa007~iBMn6~`ZE!3it*2}vy!K0LpymcSeiPrc%2%?yy8lM#Me3x zX6^o1R5K}6vo*K1sZIU4f6}8%dbi|NJ=Hk*==Z|5V;84RY`fN|Th%_ja@LPDBmnLm zfs`kKdJ!`h^?gyNxO&f#oM@g7)S~p@_{#bq)cEJNeqO{3CE3{Y(SCb|Co_Foa4i3} z!L=7J-bUa#c^a91be-9)1EOUIF?B&wBko{0x!(lzN&MIEuhoBf`P!~r1~dQ zJMf*BrecrjKd_C!3mnbS(NUZ(RYTR<-2mUmNgR)n75d(@D9*ci1)wWu_?AShDf+eL zgTXx2#z~IB`Ip4hd7n)jM*y(qKg_rk5echbK~m`5e1Hp?ERz#o79!{OLm1 z1zzx|v)FUI3WirnpI$0E6z2WX)KV>;&DeC=h+66w*=xks$eX}vS>TNA6i1Ch zwxgv8QJhYK`UEDkf(1hFe`C6Fw*w4I&HJWuOAW)F6*EsMMM zMXDpIDpr(~lzcLIE|he)ZfmL|MxPdVe@>GC3#q)kA==N^ zwTwNhiNH3A2b!l5pVD-FQi8$VeR!_!+GqND&j$z*6 z$+B=JnZ$;rtoR|b+5B|?oY2_~{m69(> zyd?JYYVP&eDxr7QUd+JVTRHYkBlllU)_&P@T31wbK=x-n`4%l(a`S~RCXyGzhcTxG zEqy)Kon}FzY1H-fdK3p$M(5^Km6cx!$i4s7Dtq|mG~<1q2PUA=H>S6QY+Wzx`P=>w zw=XAlwPY%bjr1WlMU<|^x)MpPYBub>J^}Ly!{^UqgQo?se_Ou-^Ztm$r~p9F>^hd# zSs%cA3@{eVn5A1sGL9!IPV#~--;?dVWF5GwFj#ya*XN?XshXZnERc&9HCXx{w=kb&eLFilTiaYub+Qd{Jz2#zb0jUYpk=VAb$j1Z^VrlV zwO9=wOW^COCW?+FF#v!U58*-8hbJjb68%on$ztk;nbFm8&yz7I=KO#Xru@=KGE3#b zU!ot@f9#;`++*ogALx^q>PB&!!iM0XGtStKay-?s{R0CJDHMM{zvbjUpOlS4Q9L*R zJiwv%7AbTsd|9M+;`>h>xFT^hj@Chcxr-4kOJ}D3c{O3BM9?`-u7kAUlp)EWv9~S`S9rJAkoxoRV;l4 z(%+qvcfDD|z@8(_gSk)BCV-nnPscqbmLy7hC8%j^ZL8cy1~p?+R^O;h;>-1ao!7Tw zAfTB`D~8ZSP%3=<#;T1H3eC!#!MV(f&%8Z7KpU$z>oDKN{w%E-LrGpv+t8k5VW@(k ze?Y+MF}*_%+v=~wd1z_4Q5d4x(d=g@0{{z<>kf>x;+0&LHY$l)YGpE8w?{ktzlmv( z6zxg&(DD{sx%3uYr61xB%{Do5;26##T`b>Rks8^z3bpYJgBJ|Cl!}_ zGVvWIM+5k=q%Az4emI0qSH%of*scZB@)^|Mc z5Kjdp@n1}!-@mrPgAadPdg;r{D=xS}WluQ!@5h@Rqgrt3YzYn2DdOH&cbeb)Ka>8~ zRuDf~ELEnM-JsM$4%LtmNhzot)mJ+1L*7HVFCOd*dGSgp=EbXWHlrp6i0P^Q9)!rt%96CQI$rbEZhf9bDY@=F&0-`h zcTeX%5|vb}EOnknff%?3U1ysyIsL`SSEwxIGQ26NDtSVv;dwhkC3k?jltejJQCB*C zXF{GUi5axEl}G#0b>U9TYay!k-Q?(|ojMP~#H57)-J_%R@UWw}@NZcYP?0F9iwyV; z#=yDV^6GxJt*nNi3CY#r-L}2cT(#-5?B11}DGLUMs)%Y28ztDDn>eY_EeXj1u=vRO7i5V$(yFLHKr_&)j2E;leG*6A04qx1^TP;fU< z9o_$rlvK=y*2kEe)fm^E78L$7_O0&+@3(lC^6eK}d68WY7h3V>`JYp~Zr0pBWf;e9 zc^MqMOGMOdkm@#d$1<`S(%yc&8=eMs18+?(*<`1lPI^qNHy^ZziM6g+x=&-Qt&XKv zrG2l?1^!mD*wu0IfRDI8>eLb9e|~IX8~-tDu;aelsGK?oz%9OPJ5!ad|1GWVK6;+6 zpFy_ibn+-V8|E@K9;&k@qP3aznB#>l!+v^Bg`anog5yb28W8~2Jed&WJ?n@%B>{=5 zHXk-%p~Bmk)X_eZl~m)G-zGLapIW@@(HPFxendnb`Tq`y$1MB9&L(r82}};Is7XZn zqDfz8#@!(h+p0ebyvdxwZrMp|J0-(Nk(ot4aFi-$SSzGM38M_e zq!Tc%bikBRWhpAjdv{RrqG^{re*gtT-}t#q1^v#9FNf>aH=mFYBJDt*rAaSZ5xQYM zQW3He?Y`2uto@JhR*!J*^0D87Nx+RtV}lb60KBgwOlJNisu#>%8uB?a-y;S#O>yS&cpNf3QMQ{GytZ>} zHeQ|odObUay4DmuW@{N4ZoSHg#YF*h6IjFeAQuxj*sOR;Slh`{L)V=mf{tRZ`9@8I zxGfFn9VFU%UaAANssstnWQSnXoH*HqT1op06dw~t^@iKEc*fHCg?&u6ANzA_8u|Ez zaxV6$^GBZN6Dj{@_gLWO`%Y)E$VNP1aa5sx25yH0d^gfGEuo(900SKp?J7bnxPUA^`TBY&bLv+uRsQdJkAfLdEHn) zt1&vhGV+|65i4$#EG_QgNpdfc4XA52t*HH;$qDX!{IJ0;&XIZALIZ@@H0yKjwBuu< zhu+pQYSbvS&q*dvmgvHx`=?TH@!&)>ZYoHjp6d*b4_r++7&z+?JAm8rKX~u}C}f^a zIMdYcNwgMFT~{R2<@sK#f3t;zGJ^jpoN_sAJVsdXub^9HSm1RHRX8rLy=z9+ROq5Xae&ukNu?;>Cs7UsRx*`|K^HA`U< zVO%u9HwCc$gRZk}M#u|en^U3;4wyo4=A5-oN_echDU2A82U&<5?*l9ZejmGsI;E)Vm}?HlAUt3Y*< zZsoeV9HB;!B!>Kl()#;ju3eD#PE29L&aBIREae+b1!J<>>{r;zq+D=Bm{** zkx(cDpo{WY5H!SipJ@_uPQxR(#U-zBVw6ssWWlpb%-L7}HYEP!coYB{WJkCFzKV$V zuy3yHX!!N(-$$IkV{Sivj$fL8UMM`m%|m)JXVfO3rX6ttY4Z6A^bYly#rY*Y9sl*l zxQ1KBkXa`o>sXd3&d}(7(&G4qYCz|m`crbH>cRL?(vb9vqEW-_>}m91gp{u@)%*>N zmf8&!6JkW4EgJYXsuX5yXSD(Pla(E(mU4pz5(W9?6zN&oI|O4g5umc75Yq3TQ+pBx z(WfE;Lfjabl+@{hh5g^NJo~X;dRukdJ*xVLaL$Y3xU5~-(5{6nxzX$+_;zj27F)RS z<~Y{?X}-L6{>zhv^ha9qM@+#xmlZV~qjFxO{(%fQ`bfqO%3lrrT#@&fdwm{KIP&yo z9Tq5(Dwea_;0Ii-%L*&LU1fZc82STx;CuK4+3!xPOPRd+OUfBjZF;-79JTr=hjX@m z#a3Zv@~1u9;uFQ?1}Hv~5mNR&TC2V zcW$OB1Ay2fHJM_r_C~=i2?ppKV=>6s%OG>0{pKSPW_9*<*9(1BmB7va~KY^5K~f*BCmuCpPky(bX(GsMY$5=iR{2db~s%@w!7j) zqTl?AU)__p&{$&UGUr5hD^r&-Kk&YeV31n*s65aqWlj-7Q|m894*6Pl>ZSo2=GjC{~L&P?_CG87cc?y+-8dwU2~HsX@5EYlPus0t7u&}h+i`KXd!@kmvzsR*DO?dK0RJrMmSH(n&(~LxP zr-*#@w&}B>yC25CZ-?{w4XWX#R)`m!snZBcS-njgIM;z#Lfk!Q68F&9OX6T z=*H1vO+YlAhsVc$a(SnIWQJ}po|Ya7kYx)eLB4+!p?x}+!_|To@n_lCVj}|59Mp@N z6;PpMmO6w4C#oywR174nc%dJ$O~v;}Oz&PpPQOJdEjJ>i04Bp4bwN)I&tqi@F*6c)BbJu zl#+PqJ2c!$Y(09vy&0X0mkHXVK>kl3*yE-LYxcoXI%VZmEluX!#5VLgf{41^T|Rc1 zc=*J`1X6=>RVtuFB&knsZFSOuB@uE$9OuxeFE9th$l`=Uey(tZ>dve2XSOsca4SRd z?^jV~&k}jyyg8)Z?t9xm`%oo&L>V*6$ihun@AMg?hu-~<>q>&xBV3idok8)`w`Kot zE*$oMDWEQTzBk>(VijpXoi2VkY@BonqIzG48Z4(St=P&*uemDTehe>zB{TTUy2q%2uI+n^w1M z2uCv(xeYbT-M!rVF^m~A3Y-}87BUWAChj<&K0!tW`DDCo zliGw_ycmsRPY&+@_ttDAMO%=tMys5RAof#r5+J~Hi43@P@%8G(+xoW`LJwIctw!ht zZ<5tN%z6RGe|yL}o}yJD+B&{G;><(oCF`WgBpVVM=EQ+47BA5H5=k)4ylrV$JgtU? zpL=oWPJ-{az4v}EyIPK4ds(jmT-qkbJm5eL+Z_ zBt~-`)oJ-@{J~B2*b~)O=@m~%nYIrFcD(olb?}a+UZL{}tVA$ivUyI##Z>uL#P%5G*KsdfMPg;vFgil=m)X?Ql#=kGJk{{`WI*%lB=R`2Vy96Gl`_QZM88TTbD7D-+iT&J zz-zBhPw()Z>*(CQbM)^5d-%!jv!T~Cbpke^yf{3b64}x1R_Dwo0`t)|tQJ#r^`42dCrjjLx;V))o>^~KkqDd68_O--5%Ok7Ya=D)~(l?5s)H?tAONoeN1 z&U-H_;#7guYzwb>cFrtSs-bB`*i5FT#kf5a1dP$bLBxNQ_&qLa&Dx7320=$Xv~v>} z7w=nn^f^9PGEZl+v5|}~GRs{`1Mh6a@7@$*{(skY%G9b@%Zi@faM7Y(fe{wnqhn6T zR3XE#qPRHZ&2`-Y}HarH;>vIoHs!3zYB?Y7_-@g8w(`{`pe(}ZAQ$@pRAh`?j zbE;f$I06yFgz%*9z$Zn14IRAsN!japT@nn`u0+=+z}&#l3~S;xZhH$PsHdSr`J7378<4i|Yl3lJ*O=RO^U6n2jwa=Cq{aN&IJO}X7W1c#`V$nxi;r!i_ zUc^ABaC>_@2MnT<4Gh-@>z^h&fx$9|J68J=4+h@YT4L?@sbO=mYD$NWmdApkqH-T~ zDFGlgu7L8{60CFkZV+>hXha!#)=C-6Z-r}dIRk>R)_gH!GL6&xDbfN+3bpp8*!hrV zd=P(fro6sa>TIg6@g#+3tQMT6Q;MAqhJMrdatxCyy-D=i#%6V}q_z6FY2DSO`7zul z!8}~~#hA>kIUU%~!0NKDLAlo3HuQCs^d`Pi)tK!)%xv_MUg;Bieq95D33u}$xTZJ! z>~xlpCAiUe^zu(Nk+vcZgm>e<?>`kv(gsEQa?9A)(*b ze>|ClzuzbN3sU&MU04*Jg(&idqo5J4eDz|_TJ|PY=I3kOaABWg$2C*N#J*k~I-(1O zR=*m)*Z<*cb9GiU<>+rN<9E0|QDLapG>O5;!lBL^FYjyL(>4=3dqp33)z~i7j-T_7 zB>-O>4yOpJ{c~J=Fe?(lwelF@VuU=2EP@a%9AV6Bs^DpAb%-(4S5{8Wn)1Ov`Cm5D z@2(xWP(Z<65}2ne&-p6cEeyJOpkt3TSO7&+dTN0BGyjQ>-UH1%L-WAK>S}0yU_N+k z6h4r$!tgA$UZ&|{;WS77lhB12@>kkCQG->bxuGHEWOjbF@C{``YNZUz3xo3+a34?8 zr6F?=8W}uG+5fJZA;+23ZLZ-qu(02WEe#)j*KDhHIQ47SznUau3@z_i_z4riMu)F_ zPfjrGi}TQ%rX4dnKru4wOd%D;pdlK;Z~jah&WH^=T6=lbYAA1+-nQS$12gZJcIdSD z>rWO}ndD7WtYfG5#3$vDcaQ3|4;V zJfV}u@+j>U$hFT0{TNL3jGs$@!)MD8uAqhOKoN4FBI6NhNQRkTdHvtV!N!JM5rIII z+t3PdDqT(A-%aTw$ORQT^SfmI zSF`@fk#SjlWlw1kl)AuLY|h!LeX#yD7@e1W`Mw;B-H{E#ZF=L?;n%W?=BScWosX&K|Lq2NLBNk!0UR zzR^l)^9Ye?yqp@(M#;!Sd4$GFUga6N|55ZcrRhz)k^8eIh~?_2j{2+b7B3+8XAoI2 ze{Br^PdEiVyHv>XiK#c%b8q3F(9lr--|cu58`}+976GFI4$s3&#et-QFDGCC5I*tk zmUn75ZjynTQ2;;&=WZU5*{q-~_Umn23Lt#+c7f@oc}XGZ#5JJi@@Vu@ONiv#iNPZD zcq8kis%cI}OiWB#+E`By;ju9)W;@umjQeMOA$#(1_=r+U?_sy3J-N@C2oj~AqP0CWS(mb^c5%&_4@oNA<5!WE;Y=o5Jh{hvh zuFjYLz`o`Rc$8k>;JxR`6`OcNt|)?+_1QrgFSv)zTp$(OpV)>|Q}rF#CQMCIG5vWE z7dg?+wNTv`rPXsM5$1Z<%B-TdA;r={Hmo|wcRx<^{x%9=MD#H5r zd_R%e9?SVI)6-O+bK1`A##j4TMPXn!^m%@v**Is>_alletsyo(a6V1Xo8Q=A1dReh zSbYys_rwC`cv(D&3C`TL{sdJW3C%N%5EkV2>0s0m{fjE%=xkGOMcF$#_JQeufP?Pd zx}iDU*+Xz&O3OB}j_P{BAffT=IN$GUq4Ws&WjB!Em{Ez;dLi$(ZTN%MZez@0Oo1st z!esb~cEn!U>jXGB-iV*6K^gk^Ia3pWc*;?xe+{wHkn z{QWZRQw|-{b;RzWj~rcexJ9P8Cjs}*9qt-!wO> zFgaISYw54CQ>WR-&+j(h*xjc~s`m7ka_hC&0;6>6pTJ3YMcdmD(!b#z z|K5I#JfUAgExz-S%aQAJEZS|EcYUS5q{{p?8odo~@ literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-menu-items.png new file mode 100644 index 0000000000000000000000000000000000000000..255defb707a44532ae8a3a31190394bdf9289e02 GIT binary patch literal 10649 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 z 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