Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy.
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies (see the related section above).
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies. See the *IHttpClientProxy Interface* section above.
This tutorial explains how to create a new {{if UI == "MVC"}} ASP.NET Core MVC (Razor Pages) web {{else if UI == "NG"}} Angular {{end}} application using the startup template.
This tutorial explains how to create a new web application using the [application startup template](Startup-Templates/Application.md).
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
## Setup Your Development Environment
@ -20,11 +22,11 @@ First things first! Let's setup your development environment before creating the
The following tools should be installed on your development machine:
* [Visual Studio 2019 (v16.4+)](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). <supid="a-editor">[1](#f-editor)</sup>
* [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). <supid="a-editor">[1](#f-editor)</sup>
> This document assumes that you prefer to use **{{ UI_Value }}** as the UI framework and **{{ DB_Value }}** as the database provider. For other options, please change the preference on top of this document.
### Using the ABP CLI to Create a New Project
{{else}}
Use the `new` command of the ABP CLI to create a new project:
First, you need to install the ABP CLI using the following command:
* `-u` argument specifies the UI framework, `angular` in this case.
{{ if Tiered == "Yes" }}
* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
If you've already installed, you can update it using the following command:
{{ end }}
````shell
dotnet tool update -g Volo.Abp.Cli
````
{{ end }}
{{end}}
{{ if DB == "Mongo" }}
## Create a New Project
* `-d` argument specifies the database provider, `mongodb` in this case.
Use the `new` command of the ABP CLI to create a new project:
{{ end }}
````shell
abp new Acme.BookStore{{if UI == "NG"}} -u angular{{else if UI == "Blazor"}} -u blazor --preview{{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes"}}{{if UI == "MVC"}} --tiered{{else}} --separate-identity-server{{end}}{{end}}
````
{{ if Tiered == "Yes" && UI != "NG" }}
{{if UI == "Blazor"}}
* `--tiered` argument is used to create N-tiered solution where authentication server, UI and API layers are physically separated.
> BLAZOR UI IS ONLY AVAILABLE WITH THE PREVIEW VERSION. THIS IS WHY IT IS REQUIRED TO SET THE `--PREVIEW` OPTION.
{{end}}
{{end}}
> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore.
#### ABP CLI Commands & Options
[ABP CLI document](./CLI.md) covers all of the available commands and options for the ABP CLI. This document uses the [application startup template](Startup-Templates/Application.md) to create a new web application. See the [ABP Startup Templates](Startup-Templates/Index.md) document for other templates.
> Alternatively, you can select the "Direct Download" tab from the [ABP Framework web site](https://abp.io/get-started) to create a new solution.
## The Solution Structure
{{ if Tiered == "Yes" }}
{{ if UI == "MVC" }}
After creating your project, you will have the following solution folders & files:

You will see the following solution structure when you open the `.sln` file in the Visual Studio:
* `angular` folder contains the Angular UI application.
* `aspnet-core` folder contains the backend solution.
* `--tiered` argument is used to create N-tiered solution where authentication server, UI and API layers are physically separated.
Open the `.sln` (Visual Studio solution) file under the `aspnet-core` folder:
{{ else }}

* `--separate-identity-server` argument is used to separate the identity server application from the API host application. If not specified, you will have a single endpoint on the server.
{{ end }}
> ###### About the projects in your solution
>
> Your solution may have slightly different structure based on your **UI**, **database** and other preferences.
The solution has a layered structure (based on the [Domain Driven Design](Domain-Driven-Design.md)) and also contains unit & integration test projects.
{{ if DB == "EF" }}
{{ end }}
Integration tests projects are properly configured to work with **EF Core**&**SQLite in-memory** database.
### ABP CLI Commands & Options
{{ else if DB == "Mongo" }}
Integration tests projects are properly configured to work with in-memory **MongoDB** database created per test (used [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library).
[ABP CLI document](./CLI.md) covers all of the available commands and options for the ABP CLI. This document uses the [application startup template](Startup-Templates/Application.md) to create a new web application. See the [ABP Startup Templates](Startup-Templates/Index.md) document for other templates.
{{ end }}
### The Solution Structure
> See the [application template document](Startup-Templates/Application.md) to understand the solution structure in details.
The solution has a layered structure (based on the [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects. See the [application template document](Startup-Templates/Application.md) to understand the solution structure in details.
## Create the Database
### Connection String
Check the **connection string** in the `appsettings.json` file under the {{if UI == "MVC"}}{{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}`.Web` project{{end}}{{else if UI == "NG" }}`.HttpApi.Host` project{{end}}:
Check the **connection string** in the `appsettings.json` file under the {{if Tiered == "Yes"}}`.IdentityServer` and `.HttpApi.Host` projects{{else}}{{if UI=="MVC"}}`.Web` project{{else}}`.HttpApi.Host` project{{end}}{{end}}
{{ if DB == "EF" }}
@ -163,7 +129,7 @@ Check the **connection string** in the `appsettings.json` file under the {{if UI
}
````
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 any supported DBMS. See [the Entity Framework integration document](https://docs.abp.io/en/abp/latest/Entity-Framework-Core) to learn how to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
The solution is configured to use **Entity Framework Core** with **MS SQL Server** by default. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use any supported DBMS. See [the Entity Framework integration document](Entity-Framework-Core.md) to learn how to [switch to another DBMS](Entity-Framework-Core-Other-DBMS.md).
### Apply the Migrations
@ -171,7 +137,7 @@ The solution uses the [Entity Framework Core Code First Migrations](https://docs
#### Apply Migrations Using the DbMigrator
The solution comes with a `.DbMigrator` console application which applies migrations and also seed the initial data. It is useful on development as well as on production environment.
The solution comes with a `.DbMigrator` console application which applies migrations and also **seeds the initial data**. It is useful on **development** as well as on **production** environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
@ -183,7 +149,7 @@ Right click to the `.DbMigrator` project and select **Set as StartUp Project**
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database (with the password is `1q2w3E*`) which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
#### Using EF Core Update-Database Command
@ -223,7 +189,7 @@ The solution is configured to use **MongoDB** in your local computer, so you nee
### Seed Initial Data
The solution comes with a `.DbMigrator` console application which seeds the initial data. It is useful on development as well as on production environment.
The solution comes with a `.DbMigrator` console application which **seeds the initial data**. It is useful on **development** as well as on **production** environment.
> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one.
@ -235,7 +201,7 @@ Right click to the `.DbMigrator` project and select **Set as StartUp Project**
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
> Initial [seed data](Data-Seeding.md) creates the `admin` user in the database (with the password is `1q2w3E*`) which is then used to login to the application. So, you need to use `.DbMigrator` at least once for a new database.
{{ end }}
@ -245,27 +211,27 @@ Right click to the `.DbMigrator` project and select **Set as StartUp Project**
{{ if Tiered == "Yes" }}
Ensure that the `.IdentityServer` project is the startup project. Run the application which will open a **login** page in your browser.
1. Ensure that the `.IdentityServer` project is the startup project. Run this application that will open a **login** page in your browser.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
You can login, but you cannot enter to the main application here. This is just the authentication server.
You can login, but you cannot enter to the main application here. This is **just the authentication server**.
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a **Swagger UI** in your browser.
2. Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a **Swagger UI** in your browser.

This is the API application that is used by the web application.
This is the HTTP API that is used by the web application.
Lastly, ensure that the `.Web` project is the startup project and run the application which will open a **welcome** page in your browser
3. Lastly, ensure that the `.Web` project is the startup project and run the application which will open a **welcome** page in your browser

Click to the **login** button which will redirect you to the `Identity Server` to login to the application:
Click to the **login** button which will redirect you to the *authentication server* to login to the application:

{{ else }}
{{ else # Tiered != "Yes" }}
Ensure that the `.Web` project is the startup project. Run the application which will open the **login** page in your browser:
@ -273,11 +239,11 @@ Ensure that the `.Web` project is the startup project. Run the application which

{{ end }}
{{ end # Tiered }}
{{ else if UI != "MVC" }}
{{ else # UI != "MVC" }}
#### Running the HTTP API Host (server-side)
### Running the HTTP API Host (Server Side)
{{ if Tiered == "Yes" }}
@ -287,15 +253,15 @@ Ensure that the `.IdentityServer` project is the startup project. Run the applic
You can login, but you cannot enter to the main application here. This is just the authentication server.
{{ end }}
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
{{ if Tiered == "No" }}
{{ else # Tiered == "No" }}
Ensure that the `.HttpApi.Host` project is the startup project and run the application which will open a Swagger UI:
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
{{ end }}
{{ end # Tiered }}

@ -305,12 +271,25 @@ You can see the application APIs and test them here. Get [more info](https://swa
>
> Most of the HTTP APIs require authentication & authorization. If you want to test authorized APIs, manually go to the `/Account/Login` page, enter `admin` as the username and `1q2w3E*` as the password to login to the application. Then you will be able to execute authorized APIs too.
{{ end }}
{{ end # UI }}
{{ if UI == "NG" }}
#### Running the Angular application (client-side)
{{ if UI == "Blazor" }}
Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work in most cases)
### Running the Blazor Application (Client Side)
Ensure that the `.Blazor` project is the startup project and run the application.
> Use Ctrl+F5 in Visual Studio (instead of F5) to run the application without debugging. If you don't have a debug purpose, this will be faster.
Once the application starts, click to the **Login** link on to header, which redirects you to the authentication server to enter a username and password:

{{ else if UI == "NG" }}
### Running the Angular Application (Client Side)
Go to the `angular` folder, open a command line terminal, type the `yarn` command (we suggest to the [yarn](https://yarnpkg.com/) package manager while `npm install` will also work)
```bash
yarn
@ -322,11 +301,7 @@ Once all node modules are loaded, execute `yarn start` (or `npm start`) command:
yarn start
```
This will take care of compiling your `TypeScript` code, and automatically reloading your browser.
After it finishes, `Angular Live Development Server` will be listening on localhost:4200,
open your web browser and navigate to [localhost:4200](http://localhost:4200/)
It may take a longer time for the first build. Once it finishes, it opens the Angular UI in your default browser with the [localhost:4200](http://localhost:4200/) address.

@ -338,7 +313,7 @@ Enter **admin** as the username and **1q2w3E*** as the password to login to the
If you want to include a [React Native](https://reactnative.dev/) project in your solution, add `-m react-native` (or `--mobile react-native`) argument to project creation command. This is a basic React Native startup template to develop mobile applications integrated to your ABP based backends.
See the "[Getting Started with the React Native](Getting-Started-React-Native.md)" document to learn how to configure and run the React Native application.
See the [Getting Started with the React Native](Getting-Started-React-Native.md) document to learn how to configure and run the React Native application.
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the database provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
@ -217,7 +201,7 @@ Before updating the database, read the section below to learn how to seed some i
> It's good to have some initial data in the database before running the application. This section introduces the [Data Seeding](../Data-Seeding.md) system of the ABP framework. You can skip this section if you don't want to create seed data, but it is suggested to follow it to learn this useful ABP Framework feature.
Create a class deriving from the `IDataSeedContributor` in the `*.Domain` project and copy the following code:
Create a class deriving from the `IDataSeedContributor` in the `*.Domain` project by copying the following code:
```csharp
using System;
@ -417,7 +401,7 @@ namespace Acme.BookStore.Books
### BookAppService
It is time to implement the `IBookAppService` interface. Create a new class, named `BookAppService` in the `Books` namespace (folder) of the Acme.BookStore.Application project:
It is time to implement the `IBookAppService` interface. Create a new class, named `BookAppService` in the `Books` namespace (folder) of the `Acme.BookStore.Application` project:
````csharp
using System;
@ -457,7 +441,7 @@ ABP can [**automagically**](../API/Auto-API-Controllers.md) configures your appl
### 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 by pressing `CTRL+F5` and navigate to `https://localhost:<port>/swagger/` on your browser. (Replace `<port>` with your own port number.)
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 by pressing `CTRL+F5` and navigate to `https://localhost:<port>/swagger/` on your browser. Replace `<port>` with your own port number.
You will see some built-in service endpoints as well as the `Book` service and its REST-style endpoints:
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> In this tutorial, we preferred to not add a **navigation property** to the `Author` entity (like `public Author Author { get; set; }`). This is due to follow the DDD best practices (rule: refer to other aggregates only by id). However, you can add such a navigation property and configure it for the EF Core. In this way, you don't need to write join queries while getting books with their entities (just like we will done below) which makes your application code simpler.
> In this tutorial, we preferred to not add a **navigation property** to the `Author` entity from the `Book` class (like `public Author Author { get; set; }`). This is due to follow the DDD best practices (rule: refer to other aggregates only by id). However, you can add such a navigation property and configure it for the EF Core. In this way, you don't need to write join queries while getting books with their authors (just like we will done below) which makes your application code simpler.
{{end}}
## Database & Data Migration
Added a new, required `AuthorId` property to the `Book` entity. But, what about the existing books on the database? They currently don't have `AuthorId`s and this will be a problem when we try to run the application.
Added a new, required `AuthorId` property to the `Book` entity. But, **what about the existing books** on the database? They currently don't have `AuthorId`s and this will be a problem when we try to run the application.
This is a typical migration problem and the decision depends on your case;
This is a **typical migration problem** and the decision depends on your case;
* If you haven't published your application to the production yet, you can just delete existing books in the database, or you can even delete the entire database in your development environment.
* You can do it programmatically on data migration or seed phase.
* You can update the existing data programmatically on data migration or seed phase.
* You can manually handle it on the database.
We prefer to **delete the database** {{if DB=="EF"}}(run the `Drop-Database` in the *Package Manager Console*){{end}} since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all the scenarios.
We prefer to **delete the database** {{if DB=="EF"}}(you can run the `Drop-Database` in the *Package Manager Console*){{end}} since this is just an example project and data loss is not important. Since this topic is not related to the ABP Framework, we don't go deeper for all the scenarios.
{{if DB=="EF"}}
@ -108,6 +92,8 @@ Run the following command in the Package Manager Console (of the Visual Studio)
Add-Migration "Added_AuthorId_To_Book"
````
> Ensure that the `Acme.BookStore.EntityFrameworkCore.DbMigrations` is the Default project and the `Acme.BookStore.DbMigrator` is the startup project, as always.
This should create a new migration class with the following code in its `Up` method:
````csharp
@ -222,6 +208,8 @@ namespace Acme.BookStore
The only change is that we set the `AuthorId` properties of the `Book` entities.
> Delete existing books or delete the database before executing the `DbMigrator`. See the *Database & Data Migration* section above for more info.
{{if DB=="EF"}}
You can now run the `.DbMigrator` console application to **migrate** the **database schema** and **seed** the initial data.
@ -299,7 +287,7 @@ namespace Acme.BookStore.Books
}
````
This will be used in a new method will be added to the `IBookAppService`.
This will be used in a new method that will be added to the `IBookAppService`.
### IBookAppService
@ -1068,4 +1056,112 @@ Open the `/src/app/book/book.component.html` and add the following form group ju
That's all. Just run the application and try to create or edit an author.
{{end}}
{{if UI == "Blazor"}}
### The Book List
It is very easy to show the *Author Name* in the book list. Open the `/Pages/Books.razor` file in the `Acme.BookStore.Blazor` project and add the following `DataGridColumn` definition just after the `Name` (book name) column:
````xml
<DataGridColumnTItem="BookDto"
Field="@nameof(BookDto.AuthorName)"
Caption="@L["Author"]"></DataGridColumn>
````
When you run the application, you can see the *Author* column on the table:
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
@ -115,7 +99,7 @@ We will use these dynamic proxy functions in the next sections to communicate to
## Localization
Before starting to the UI development, we first want to prepare the localization texts (you normally do when needed while developing your application).
Before starting to the UI development, we first want to prepare the localization texts (you normally do this when needed while developing your application).
Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project:
@ -341,9 +325,7 @@ You can run the application! The final UI of this part is shown below:
This is a fully working, server side paged, sorted and localized table of books.
{{end}}
{{if UI == "NG"}}
{{else if UI == "NG"}}
## Install NPM packages
@ -551,7 +533,137 @@ Now you can see the final result on your browser:

{{end}}
{{else if UI == "Blazor"}}
## Create a Books Page
It's time to create something visible and usable! Right click to the `Pages` folder under the `Acme.BookStore.Blazor` project and add a new **razor component**, named `Books.razor`:
Replace the contents of this component as shown below:
````html
@page "/books"
<h2>Books</h2>
@code {
}
````
### Add Books Page to the Main Menu
Open the `BookStoreMenuContributor` class in the `Blazor` project add the following code to the end of the `ConfigureMainMenuAsync` method:
````csharp
context.Menu.AddItem(
new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
).AddItem(
new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/books"
)
)
);
````
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu:
When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page.
### Book List
We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including the Bootstrap.
ABP Framework provides a generic base class, `BlazoriseCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `BlazoriseCrudPageBase` to automate the standard CRUD stuff.
Open the `Books.razor` and replace the content as the following:
> If you see some syntax errors, you can ignore them if your application property built and run. Visual Studio still has some bugs with Blazor.
* Inherited from the `BlazoriseCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base blass.
* Injected `IStringLocalizer<BookStoreResource>` (as `L` object) and used for localization.
While the code above pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better.
#### About the BlazoriseCrudPageBase
We will continue to benefit from the `BlazoriseCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications.
## Run the Final Application
You can run the application! The final UI of this part is shown below:
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
@ -1175,6 +1159,392 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir
{{end}}
{{if UI == "Blazor"}}
## Creating a New Book
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `BlazoriseCrudPage`, we only need to develop the view part.
### Add "New Button" Button
Open the `Books.razor` and replace the `<CardHeader>` section with the following code:
* `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` method are defined by the base class. See the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
That's all. Run the application and try to add a new book:
The base `BlazoriseCrudPage` uses the [object to object mapping](../Object-To-Object-Mapping.md) system to convert an incoming `BookDto` object to a `CreateUpdateBookDto` object. So, we need to define the mapping.
Open the `BookStoreBlazorAutoMapperProfile` inside the `Acme.BookStore.Blazor` project and change the content as the following:
````csharp
using Acme.BookStore.Books;
using AutoMapper;
namespace Acme.BookStore.Blazor
{
public class BookStoreBlazorAutoMapperProfile : Profile
{
public BookStoreBlazorAutoMapperProfile()
{
CreateMap<BookDto,CreateUpdateBookDto>();
}
}
}
````
* We've just added the `CreateMap<BookDto, CreateUpdateBookDto>();` line to define the mapping.
### Test the Editing Modal
You can now run the application and try to edit a book.
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
@ -62,7 +46,9 @@ This part is also recorded as a video tutorial and **<a href="https://www.youtub
This part covers the **server side** tests. There are several test projects in the solution:
> Test projects slightly differs based on your UI and Database selection. For example, if you select MongoDB, then the `Acme.BookStore.EntityFrameworkCore.Tests` will be `Acme.BookStore.MongoDB.Tests`.
Each project is used to test the related project. Test projects use the following libraries for testing:
@ -72,11 +58,11 @@ Each project is used to test the related project. Test projects use the followin
{{if DB=="EF"}}
> The test projects are configured to use **SQLite in-memory** as the database. A separate database instance is created and seeded (with the data seed system) to prepare a fresh database for every test.
> The test projects are configured to use **SQLite in-memory** as the database. A separate database instance is created and seeded (with the [data seed system](../Data-Seeding.md)) to prepare a fresh database for every test.
{{else if DB=="Mongo"}}
> **[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)** library is used to mock the MongoDB database. A separate database instance is created and seeded (with the data seed system) to prepare a fresh database for every test.
> **[Mongo2Go](https://github.com/Mongo2Go/Mongo2Go)** library is used to mock the MongoDB database. A separate database instance is created and seeded (with the [data seed system](../Data-Seeding.md)) to prepare a fresh database for every test.
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
{{if UI == "MVC" && DB == "EF"}}
@ -145,6 +129,8 @@ Go to the *Administration -> Identity -> Roles* page, select *Permissions* actio
Grant the permissions you want and save the modal.
> **Tip**: New permissions are automatically granted to the admin role if you run the `Acme.BookStore.DbMigrator` application.
## Authorization
Now, you can use the permissions to authorize the book management.
@ -186,6 +172,8 @@ namespace Acme.BookStore.Books
Added code to the constructor. Base `CrudAppService` automatically uses these permissions on the CRUD operations. This makes the **application service** secure, but also makes the **HTTP API** secure since this service is automatically used as an HTTP API as explained before (see [auto API controllers](../API/Auto-API-Controllers.md)).
> You will see the declarative authorization, using the `[Authorize(...)]` attribute, later while developing the author management functionality.
{{if UI == "MVC"}}
### Razor Page
@ -327,6 +315,64 @@ if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
}
````
You also need to add `async` keyword to the `ConfigureMenuAsync` method and re-arrange the return values. The final `BookStoreMenuContributor` class should be the following:
````csharp
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Acme.BookStore.Localization;
using Acme.BookStore.MultiTenancy;
using Acme.BookStore.Permissions;
using Volo.Abp.TenantManagement.Web.Navigation;
using Volo.Abp.UI.Navigation;
namespace Acme.BookStore.Web.Menus
{
public class BookStoreMenuContributor : IMenuContributor
{
public async Task ConfigureMenuAsync(MenuConfigurationContext context)
var l = context.GetLocalizer<BookStoreResource>();
context.Menu.Items.Insert(0, new ApplicationMenuItem("BookStore.Home", l["Menu:Home"], "~/"));
var bookStoreMenu = new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
);
context.Menu.AddItem(bookStoreMenu);
//CHECK the PERMISSION
if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
{
bookStoreMenu.AddItem(new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/Books"
));
}
}
}
}
````
{{else if UI == "NG"}}
### Angular Guard Configuration
@ -412,6 +458,197 @@ Open the `/src/app/book/book.component.html` file and replace the edit and delet
{{end}}
{{else if UI == "Blazor"}}
### Authorize the Razor Component
Open the `/Pages/Books.razor` file in the `Acme.BookStore.Blazor` project and add an `Authorize` attribute just after the `@page` directive, as shown below:
Adding this attribute prevents to enter this page if the current hasn't logged in or hasn't granted for the given permission. In case of attempt, the user is redirected to the login page.
### Show/Hide the Actions
The book management page has a *New Book* button and *Edit* and *Delete* actions for each book. We should hide these buttons/actions if the current user has not granted for the related permissions.
#### Inject the IAuthorizationService
Inject the `IAuthorizationService` into the `Books.razor`:
We will use these `bool` fields to check the permissions.
> **Blazor Tip**: While adding the C# code into a `@code` block is fine for small code parts, it is suggested to use the code behind approach to develop a more maintainable code base when the code block becomes longer. We will use this approach for the authors part.
#### Hide the New Book Button
Wrap the *New Book* button by an `if` block as shown below:
You can run and test the permissions. Remove a book related permission from the admin role to see the related button/action disappears from the UI.
However, ABP Framework caches the permissions of the current user in the client side. So, when you change a permission for yourself, you need to manually **refresh the page** to take the effect. If you don't refresh and try to use the prohibited action you get an HTTP 403 (forbidden) response from the server.
> Changing a permission for a role or user immediately available on the server side. So, this cache system doesn't cause any security problem.
### Menu Item
Even we have secured all the layers of the book management page, it is still visible on the main menu of the application. We should hide the menu item if the current user has no permission.
Open the `BookStoreMenuContributor` class in the `Acme.BookStore.Blazor` project, find the code block below:
````csharp
context.Menu.AddItem(
new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
).AddItem(
new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/books"
)
)
);
````
And replace this code block with the following:
````csharp
var bookStoreMenu = new ApplicationMenuItem(
"BooksStore",
l["Menu:BookStore"],
icon: "fa fa-book"
);
context.Menu.AddItem(bookStoreMenu);
//CHECK the PERMISSION
if (await context.IsGrantedAsync(BookStorePermissions.Books.Default))
{
bookStoreMenu.AddItem(new ApplicationMenuItem(
"BooksStore.Books",
l["Menu:Books"],
url: "/books"
));
}
````
You also need to add `async` keyword to the `ConfigureMenuAsync` method and re-arrange the return values. The final `BookStoreMenuContributor` class should be the following:
````csharp
using System.Threading.Tasks;
using Acme.BookStore.Localization;
using Acme.BookStore.Permissions;
using Volo.Abp.UI.Navigation;
namespace Acme.BookStore.Blazor
{
public class BookStoreMenuContributor : IMenuContributor
{
public async Task ConfigureMenuAsync(MenuConfigurationContext context)
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
## Introduction
@ -242,16 +226,7 @@ This is a unique string represents the error code thrown by your application and
"BookStore:00001": "There is already an author with the same name: {name}"
````
Then open the `BookStoreDomainSharedModule` and add the following code block inside the `ConfigureServices` method:
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
## Introduction
@ -86,7 +70,7 @@ This is just like done for the `Book` entity before, so no need to explain again
## Create a new Database Migration
Open the **Package Manager Console** on Visual Studio and ensure that the **Default project** is `Acme.BookStore.EntityFrameworkCore.DbMigrations` in the Package Manager Console, as shown on the picture below. Also, set the `Acme.BookStore.Web` as the startup project (right click it on the solution explorer and click to "Set as Startup Project").
Open the **Package Manager Console** on Visual Studio and ensure that the **Default project** is `Acme.BookStore.EntityFrameworkCore.DbMigrations` in the Package Manager Console, as shown on the picture below. Also, set the `Acme.BookStore.Web`(or `Acme.BookStore.HttpApi.Host`, depending on your solution) as the **startup project** (right click it on the solution explorer and click to "Set as Startup Project").
Run the following command to create a new database migration:
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,9 +28,10 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> **EF Core tip**: Entity Framework Core has a **change tracking** system and **automatically saves** any change to an entity at the end of the unit of work (You can simply think that the ABP Framework automatically calls `SaveChanges` at the end of the method). So, it will work as expected even if you don't call the `_authorRepository.UpdateAsync(...)` in the end of the method. If you don't consider to change the EF Core later, you can just remove this line.
> **EF Core Tip**: Entity Framework Core has a **change tracking** system and **automatically saves** any change to an entity at the end of the unit of work (You can simply think that the ABP Framework automatically calls `SaveChanges` at the end of the method). So, it will work as expected even if you don't call the `_authorRepository.UpdateAsync(...)` in the end of the method. If you don't consider to change the EF Core later, you can just remove this line.
In this tutorial series, you will build an ABP based web application named `Acme.BookStore`. This application is used to manage a list of books and their authors. It is developed using the following technologies:
* **{{DB_Text}}** as the ORM provider.
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
@ -45,18 +28,19 @@ This tutorial is organized as the following parts;
### Download the Source Code
This tutorials has multiple versions based on your **UI** and **Database** preferences. We've prepared two combinations of the source code to be downloaded:
This tutorial has multiple versions based on your **UI** and **Database** preferences. We've prepared a few combinations of the source code to be downloaded:
* [MVC (Razor Pages) UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
## Introduction
This part explains how to create a CRUD page for the `Author` entity introduced in previous parts.
This part explains how to create a CRUD page for the `Author` entity introduced in the previous parts.
{{if UI == "MVC"}}
## The Book List Page
## The Authors List Page
Create a new razor page, `Index.cshtml` under the `Pages/Authors` folder of the `Acme.BookStore.Web` project and change the content as given below.
@ -509,7 +493,7 @@ That's all! You can run the application and try to edit an author.
{{else if UI == "NG"}}
## The Author List Page, Create & Delete Authors
## The Author Management Page
Run the following command line to create a new module, named `AuthorModule` in the root folder of the angular application:
@ -848,6 +832,353 @@ That's all! This is a fully working CRUD page, you can create, edit and delete a
{{end}}
{{if UI == "Blazor"}}
## The Author Management Page
### Authors Razor Component
Create a new Razor Component Page, `/Pages/Authors.razor`, in the `Acme.BookStore.Blazor` project with the following content:
* This code is similar to the `Books.razor`, except it doesn't inherit from the `BlazorisePageBase`, but uses its own implementation.
* Injects the `IAuthorAppService` to consume the server side HTTP APIs from the UI. We can directly inject application service interfaces and use just like regular method calls by the help of [Dynamic C# HTTP API Client Proxy System](../API/Dynamic-CSharp-API-Clients.md), which performs REST API calls for us. See the `Authors` class below to see the usage.
* Injects the `IAuthorizationService` to check [permissions](../Authorization.md).
* Injects the `IObjectMapper` for [object to object mapping](../Object-To-Object-Mapping.md).
Create a new code behind file, `Authors.razor.cs`, under the `Pages` folder, with the following content:
This class typically defines the properties and methods used by the `Authors.razor` page.
### Object Mapping
`Authors` class uses the `IObjectMapper` in the `OpenEditAuthorModal` method. So, we need to define this mapping.
Open the `BookStoreBlazorAutoMapperProfile.cs` in the `Acme.BookStore.Blazor` project and add the following mapping code in the constructor:
````csharp
CreateMap<AuthorDto,UpdateAuthorDto>();
````
You will need to declare a `using Acme.BookStore.Authors;` statement to the beginning of the file.
### Add to the Main Menu
Open the `BookStoreMenuContributor.cs` in the `Acme.BookStore.Blazor` project and add the following code to the end of the `ConfigureMainMenuAsync` method:
````csharp
if (await context.IsGrantedAsync(BookStorePermissions.Authors.Default))
{
bookStoreMenu.AddItem(new ApplicationMenuItem(
"BooksStore.Authors",
l["Menu:Authors"],
url: "/authors"
));
}
````
### Localizations
We should complete the localizations we've used above. Open the `en.json` file under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project and add the following entries:
````json
"Menu:Authors": "Authors",
"Authors": "Authors",
"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?",