Browse Source

Grammer fixes in part 05

pull/20865/head
Halil İbrahim Kalkan 2 years ago
committed by GitHub
parent
commit
e3b4c410a0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 56
      docs/en/tutorials/modular-crm/part-05.md

56
docs/en/tutorials/modular-crm/part-05.md

@ -14,13 +14,13 @@
}
````
In the previous part, we've created a custom Ordering module and installed it into the main application. However, the Ordering module has no functionality now. In this part, we will create an `Order` entity and add functionality to create and list the orders.
In the previous part, we created a custom Ordering module and installed it into the main application. However, the Ordering module has no functionality now. In this part, we will create an `Order` entity and add functionality to create and list the orders.
## Creating an `Order` Entity
Open the `ModularCrm.Ordering` .NET solution in your IDE.
> Tip: You can open the folder of a module's .NET solution by right-clicking the related module in ABP Studio, selecting the *Open with* -> *Explorer* command.
> Tip: You can open the folder of a module's .NET solution by right-clicking the related module in ABP Studio and selecting the *Open with* -> *Explorer* command.
The following figure shows the `ModularCrm.Ordering` module in the *Solution Explorer* panel of Visual Studio:
@ -32,9 +32,9 @@ As you see in the preceding figure, the solution structure is very minimal. It a
![visual-studio-ordering-ui-package-dependency](images/visual-studio-ordering-ui-package-dependency.png)
We will create an [entity class](../../framework/architecture/domain-driven-design/entities.md) and ABP defines base entity classes and other related infrastructure in the [`Volo.Abp.Ddd.Domain`](https://abp.io/package-detail/Volo.Abp.Ddd.Domain) package. So, we need to add a reference to that NuGet package.
We will create an [entity class](../../framework/architecture/domain-driven-design/entities.md). ABP defines base entity classes and other related infrastructure in the [`Volo.Abp.Ddd.Domain`](https://abp.io/package-detail/Volo.Abp.Ddd.Domain) package. So, we need to add a reference to that NuGet package.
You can add that package and arrange the [module](../../framework/architecture/modularity/basics.md) class dependency manually. However, here we will use ABP Studio as a more practical way.
You can add that package and manually arrange the [module](../../framework/architecture/modularity/basics.md) class dependency. However, we will use ABP Studio as a more practical option.
Return to ABP Studio, right-click the `ModularCrm.Ordering` package in the *Solution Explorer* and select the *Add Package Reference* command:
@ -44,9 +44,9 @@ That command opens a dialog to add a new package reference:
![abp-studio-add-nuget-package-reference](images/abp-studio-add-nuget-package-reference.png)
Select the *NuGet* tab, type `Volo.Abp.Ddd.Domain` as the *Package name* and write the version of the package you want to install. Please be sure that you are installing exactly the same version with the other ABP packages you are already using. In future versions, ABP Studio will provide an easier way to select the package and its version.
Select the *NuGet* tab, type `Volo.Abp.Ddd.Domain` as the *Package name* and write the version of the package you want to install. Please be sure that you are installing the same version as the other ABP packages you are already using. ABP Studio will provide an easier way to select the package and its version in future ABP versions.
Click the *OK* button and then you can check the *Packages* under the `ModularCrm.Ordering` module *Dependencies* to see the `Volo.Abp.Ddd.Domain` package is installed:
Click the *OK* button. Now you can check the *Packages* under the `ModularCrm.Ordering` module *Dependencies* to see the `Volo.Abp.Ddd.Domain` package is installed:
![abp-studio-added-ddd-domain-package](images/abp-studio-added-ddd-domain-package.png)
@ -70,11 +70,11 @@ namespace ModularCrm.Ordering.Entities
}
````
We are allowing to place only a single product within an order. In a real-world application, the `Order` entity would be much more complex. However, the complexity of the `Order` entity doesn't effect modularity, so we keep it simple to focus on modularity in this tutorial. We are inheriting from the [`CreationAuditedAggregateRoot` class](../../framework/architecture/domain-driven-design/entities.md) since I want to know when an order has been created and who has created it.
We allow users to place only a single product within an order. The `Order` entity would be much more complex in a real-world application. However, the complexity of the `Order` entity doesn't affect modularity, so we keep it simple to focus on modularity in this tutorial. We are inheriting from the [`CreationAuditedAggregateRoot` class](../../framework/architecture/domain-driven-design/entities.md) since I want to know when an order has been created and who has created it.
### Adding an `OrderState` Enumeration
We used an `OrderState` enumeration that is not defined yet. Open an `Enums` folder in the `ModularCrm.Ordering.Contracts` project and create an `OrderState.cs` file inside it:
We used an `OrderState` enumeration that has not yet been defined. Open an `Enums` folder in the `ModularCrm.Ordering.Contracts` project and create an `OrderState.cs` file inside it:
````csharp
namespace ModularCrm.Ordering.Contracts.Enums;
@ -87,7 +87,7 @@ public enum OrderState : byte
}
````
Final structure of the Ordering module should be similar to the following figure in your IDE:
The final structure of the Ordering module should be similar to the following figure in your IDE:
![visual-studio-order-entity](images/visual-studio-order-entity.png)
@ -97,7 +97,7 @@ The `Order` entity has been created. Now, we need to configure the database mapp
### Installing the Entity Framework Core Package
> In this section, we will install the [`Volo.Abp.EntityFrameworkCore`](https://abp.io/package-detail/Volo.Abp.EntityFrameworkCore) package to the Ordering module. That package is DBMS-independent and leaves the DBMS selection to the final application. If you want, you can install DBMS-specific package instead. For example, you can install the [`Volo.Abp.EntityFrameworkCore.SqlServer`](https://abp.io/package-detail/Volo.Abp.EntityFrameworkCore.SqlServer) package if you are using SQL server and want to make SQL Server specific configuration for your module's database.
> In this section, we will install the [`Volo.Abp.EntityFrameworkCore`](https://abp.io/package-detail/Volo.Abp.EntityFrameworkCore) package to the Ordering module. That package is DBMS-independent and leaves the DBMS selection to the final application. If you want, you can install a DBMS-specific package instead. For example, you can install the [`Volo.Abp.EntityFrameworkCore.SqlServer`](https://abp.io/package-detail/Volo.Abp.EntityFrameworkCore.SqlServer) package if you are using SQL Server and want to make SQL Server specific configuration for your module's database.
> You can search for other packages on the [abp.io/packages](https://abp.io/packages) page.
Stop the web application if it is still running. Return to ABP Studio, right-click the `ModularCrm.Ordering` package on the *Solution Explorer* panel and select the *Add Package Reference* command:
@ -112,7 +112,7 @@ Once you click the *OK* button, the NuGet package reference is added.
### Defining the Database Mappings
Entity Framework Core requires to define a `DbContext` class as the main object for the database mapping. We want to use the main application's `DbContext` object. In that way, we can control the database migrations in a single point, ensure database transactions on multi-module operations and establish relations between database tables of different modules. However, the Ordering module can not use the main application's `DbContext` object, because it doesn't depend on the main application and we don't want to establish such a dependency.
Entity Framework Core requires defining a `DbContext` class as the main object for the database mapping. We want to use the main application's `DbContext` object. That way, we can control the database migrations at a single point, ensure database transactions on multi-module operations, and establish relations between database tables of different modules. However, the Ordering module can not use the main application's `DbContext` object because it doesn't depend on the main application, and we don't want to establish such a dependency.
As a solution, we will define a `DbContext` interface in the Ordering module which is then implemented by the main module's `DbContext`.
@ -132,7 +132,7 @@ namespace ModularCrm.Ordering.Data
}
````
Now, we can inject and use the `IOrderingDbContext` in the Ordering module. Actually, we will not directly use that interface most of the time. Instead, we will use ABP's [repositories](../../framework/architecture/domain-driven-design/repositories.md).
We can inject and use the `IOrderingDbContext` in the Ordering module. However, we will not usually directly use that interface. Instead, we will use ABP's [repositories](../../framework/architecture/domain-driven-design/repositories.md), which internally uses that interface.
It is best to configure the database table mapping for the `Order` entity in the Ordering module. We will create an extension method that will be called by the main application later. Create a class named `OrderingDbContextModelCreatingExtensions` in the same `Data` folder:
@ -155,7 +155,7 @@ namespace ModularCrm.Ordering.Data
//Configure table name
b.ToTable("Orders");
//Always call this method to setup base entity properties
//Always call this method to set base entity properties
b.ConfigureByConvention();
//Properties of the entity
@ -176,7 +176,7 @@ Open the main application's solution in your IDE, find the `ModularCrmDbContext`
[ReplaceDbContext(typeof(IOrderingDbContext))]
````
`ReplaceDbContext` attribute makes it possible to use the `ModularCrmDbContext` class in the services in the Ordering module.
The `ReplaceDbContext` attribute allows the use of the `ModularCrmDbContext` class in the services in the Ordering module.
**(2)** Implement the `IOrderingDbContext` by the `ModularCrmDbContext` class:
@ -204,11 +204,11 @@ protected override void OnModelCreating(ModelBuilder builder)
}
````
In this way, `ModularCrmDbContext` can be used by the Ordering module over the `IProductsDbContext` interface. This part is only needed for one time for a module. Next time, you can just add a new database migration as explained in the next section.
In this way, the Ordering module can use' ModularCrmDbContext' over the `IProductsDbContext` interface. This part is only needed once for a module. Next time, you can add a new database migration, as explained in the next section.
#### Add a Database Migration
Now, we can add a new database migration. You can use Entity Framework Core's `Add-Migration` (or `dotnet ef migrations add`) terminal command, but we will use ABP Studio's shortcut UI in this tutorial.
Now, we can add a new database migration. You can use Entity Framework Core's `Add-Migration` (or `dotnet ef migrations add`) terminal command, but in this tutorial, we will use ABP Studio's shortcut UI.
Ensure that the solution has built. You can right-click the `ModularCrm` (under the `main` folder) on ABP Studio *Solution Runner* and select the *Dotnet CLI* -> *Graph Build* command.
@ -220,7 +220,7 @@ The *Add Migration* command opens a new dialog to get a migration name:
![abp-studio-entity-framework-core-add-migration-order](images/abp-studio-entity-framework-core-add-migration-order.png)
Once you click the *OK* button, a new database migration class is added into the `Migrations` folder of the `ModularCrm.EntityFrameworkCore` project:
Once you click the *OK* button, a new database migration class is added to the `Migrations` folder of the `ModularCrm.EntityFrameworkCore` project:
![visual-studio-new-migration-class-2](images/visual-studio-new-migration-class-2.png)
@ -234,7 +234,7 @@ After the operation completes, you can check your database to see the new `Order
## Creating the User Interface
Since this is a non-layered module, we can directly use entities and repositories on the user interface. If you think that is not a good practice, then use the layered module template as we've already done for the *Products* module. But for the Ordering module, we will keep it very simple for this tutorial to show it is also possible.
Since this is a non-layered module, we can use entities and repositories directly on the user interface. If you think that is not a good practice, then use the layered module template as we've already done for the *Products* module. But for the Ordering module, we will keep it very simple for this tutorial to show it is also possible.
### Creating a `_ViewImports.cshtml` File
@ -247,7 +247,7 @@ Open the `ModularCrm.Ordering` .NET solution in your favorite IDE, locate the `M
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
That file simply imports some tag helpers from ASP.NET Core and ABP. The `Pages` folder should be like the following figure:
That file imports some tag helpers from ASP.NET Core and ABP. The `Pages` folder should be like the following figure:
![visual-studio-pages-folder](images/visual-studio-pages-folder.png)
@ -284,7 +284,7 @@ namespace ModularCrm.Ordering.Pages.Orders
}
````
Here, we are injecting a repository to query `Order` entities from database to show on the page. Open the `Index.cshtml` file and replace the content with the following code block:
Here, we are injecting a repository to query `Order` entities from the database to show on the page. Open the `Index.cshtml` file and replace the content with the following code block:
````html
@page
@ -308,7 +308,7 @@ Here, we are injecting a repository to query `Order` entities from database to s
</abp-card>
````
This page simply shows a list of orders on the UI. We haven't created a UI to create new orders, and we will not do it to keep this tutorial simple. If you want to learn how to create advanced UIs with ABP, please follow the [Book Store tutorial](../book-store/index.md).
This page shows a list of orders on the UI. We haven't created a UI to create new orders, and we will not do it to keep this tutorial simple. If you want to learn how to create advanced UIs with ABP, please follow the [Book Store tutorial](../book-store/index.md).
### Creating Some Sample Data
@ -320,21 +320,21 @@ You can get `ProductId` values from the `Products` table and [generate](https://
### Building the Application
Now, we will run the application to see the result. First of all, stop the application if it is already running. Then open the *Solution Runner* panel, right-click the `ModularCrm.Web` application, select the *Build* -> *Graph Build* command:
Now, we will run the application to see the result. Please stop the application if it is already running. Then open the *Solution Runner* panel, right-click the `ModularCrm.Web` application, and select the *Build* -> *Graph Build* command:
![abp-studio-solution-runner-graph-build](images/abp-studio-solution-runner-graph-build.png)
We've performed a graph build since we've made a change on a module and only building the main application is not enough. *Graph Build* command also builds the depended modules if it is necessary. Alternatively, you could build the Ordering module first (on ABP Studio or on your IDE), then Build right-click the `ModularCrm.Web` application and select the *Run* -> *Build & Start*. This approach can be faster if you have too many modules and you made change in one of the modules.
We've performed a graph build since we've made a change on a module, and more than building the main application is needed. *Graph Build* command also builds the depended modules if necessary. Alternatively, you could build the Ordering module first (on ABP Studio or your IDE), then right-click the `ModularCrm.Web` application and select the *Run* -> *Build & Start*. This approach can be faster if you have too many modules and you make a change in one of the modules.
### Running the Application
Run the main application on ABP Studio, manually type `/Orders` to end of your application's URL to open the *Orders* page:
Run the main application on ABP Studio, manually type `/Orders` to the end of your application's URL to open the *Orders* page:
![abp-studio-solution-runner-orders-page](images/abp-studio-solution-runner-orders-page.png)
Great! We can see the list of orders. However, there are two problems:
1. We don't see the Orders item on the main menu. This is because we haven't configured the [navigation menu system](../../framework/ui/mvc-razor-pages/navigation-menu.md) yet.
1. The Order page has no menu item on the main menu. This is because we haven't configured the [navigation menu system](../../framework/ui/mvc-razor-pages/navigation-menu.md) yet.
2. We see Product's GUID ID instead of its name. This is because the Ordering module has no integration with the Products module and doesn't have access to Product module's database to perform a JOIN query.
We will solve the second problem in the [next part](part-06.md), but we can easily add a menu item for the Orders page now.
@ -373,7 +373,7 @@ namespace ModularCrm.Ordering
}
````
`OrderingMenuContributor` implements the `IMenuContributor` interface which forces us to implement the `ConfigureMenuAsync` method. In that method, we can just manipulate the menu items (add new menu items, remove existing menu items or change properties of existing menu items). The `ConfigureMenuAsync` method is executed whenever the menu is rendered on the UI, so you can dynamically decide how to manipulate the menu items.
`OrderingMenuContributor` implements the `IMenuContributor` interface, which forces us to implement the `ConfigureMenuAsync` method. In that method, we can manipulate the menu items (add new menu items, remove existing menu items or change the properties of existing menu items). The `ConfigureMenuAsync` method is executed whenever the menu is rendered on the UI, so you can dynamically decide how to manipulate the menu items.
After creating such a class, we should configure the `AbpNavigationOptions` to add that contributor. Open the `OrderingWebModule` class in the `ModularCrm.Ordering` project and add the following configuration code into the `ConfigureServices` method (if there is no `ConfigureServices` method, first create it as shown below):
@ -397,6 +397,6 @@ The *Orders* menu item is added under the *Products* menu item.
## Summary
In this part of the *Modular CRM* tutorial, we've built the functionality inside the Ordering module that we'd created in the [previous part](part-04.md). Since we've created the Ordering module from scratch (with the *Empty Module* template), we had to implement many aspects manually, added ABP packages, created some configuration classes, etc. It is good to do all these manually for one time for learning the things, but it is better to use the other module templates (that pre-configures the fundamentals for us) for a more comfortable development experience.
In this part of the *Modular CRM* tutorial, we've built the functionality inside the Ordering module we created in the [previous part](part-04.md). Since we've created the Ordering module from scratch (with the *Empty Module* template), we had to implement many aspects manually, add ABP packages, create some configuration classes, etc. It is good to do all these manually for one time to learn the things, but it is better to use the other module templates (that pre-configure the fundamentals for us) for a more comfortable development experience.
In the next part, we will work for establishing communication between the Orders module and the Products module.
In the next part, we will work on establishing communication between the Orders module and the Products module.

Loading…
Cancel
Save