In this chapter, I continue to build the SportsStore application in order to give the site administrator a way to manage orders and products. In this chapter, I use Blazor to create administration features. Blazor is a new addition to ASP.NET Core, and it combines client-side JavaScript code with server-side code executed by ASP.NET Core, connected by a persistent HTTP connection. I describe Blazor in detail in Chapters 32–35, but it is important to understand that the Blazor model is not suited to all projects. (I use Blazor Server in this chapter, which is a supported part of the ASP.NET Core platform. There is also Blazor WebAssembly, which is, at the time of writing, experimental and runs entirely in the browser. I describe Blazor WebAssembly in Chapter 36.)
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/apress/pro-asp.net-core-3. See Chapter 1 for how to get help if you have problems running the examples.
Preparing Blazor Server
Enabling Blazor in the Startup.cs File in the SportsStore Folder
The AddServerSideBlazor method creates the services that Blazor uses, and the MapBlazorHub method registers the Blazor middleware components. The final addition is to finesse the routing system to ensure that Blazor works seamlessly with the rest of the application.
Creating the Imports File
Blazor requires its own imports file to specify the namespaces that it uses. Create the Pages/Admin folder and add to it a file named _Imports.razor with the content shown in Listing 10-2. (If you are using Visual Studio, you can use the Razor Components template to create this file.)
The conventional location for Blazor files is within the Pages folder, but Blazor files can be defined anywhere in the project. In Part 4, for example, I used a folder named Blazor to help emphasize which features were provided by Blazor and which by Razor Pages.
The Contents of the _Imports.razor File in the SportsStore/Pages/Admin Folder
The first four @using expressions are for the namespaces required for Blazor. The last two expressions are for convenience in the examples that follow because they will allow me to use Entity Framework Core and the classes in the Models namespace.
Creating the Startup Razor Page
The Contents of the Index.cshtml File in the SportsStore/Pages/Admin Folder
The component element is used to insert a Razor Component in the output from the Razor Page. Razor Components are the confusingly named Blazor building blocks, and the component element applied in Listing 10-3 is named Routed and will be created shortly. The Razor Page also contains a script element that tells the browser to load the JavaScript file that Blazor Server uses. Requests for this file are intercepted by the Blazor Server middleware, and you don’t need to explicitly add the JavaScript file to the project.
Creating the Routing and Layout Components
The Contents of the Routed.razor File in the SportsStore/Pages/Admin Folder
The content of this component is described in detail in Part 4 of this book, but, for this chapter, it is enough to know that the component will use the browser’s current URL to locate a Razor Component that can be displayed to the user. If no matching component can be found, then an error message is displayed.
The Contents of the AdminLayout.razor File in the SportsStore/Pages/Admin Folder
Blazor uses Razor syntax to generate HTML but introduces its own directives and features. This layout renders a two-column display with Product and Order navigation buttons, which are created using NavLink elements. These elements apply a built-in Razor Component that changes the URL without triggering a new HTTP request, which allows Blazor to respond to user interaction without losing the application state.
Creating the Razor Components
The Contents of the Products.razor File in the SportsStore/Pages/Admin Folder
The Contents of the Orders.razor File in the SportsStore/Pages/Admin Folder
Checking the Blazor Setup
To make sure that Blazor is working correctly, start ASP.NET Core and request http://localhost:5000/admin. This request will be handled by the Index Razor Page in the Pages/Admin folder, which will include the Blazor JavaScript file in the content it sends to the browser. The JavaScript code will open a persistent HTTP connection to the ASP.NET Core server, and the initial Blazor content will be rendered, as shown in Figure 10-1.
Microsoft has not yet released the tools required to test Razor Components, which is why there are no unit testing examples in this chapter.
Managing Orders
Now that Blazor has been set up and tested, I am going to start implementing administration features. In the previous chapter, I added support for receiving orders from customers and storing them in a database. In this section, I am going to create a simple administration tool that will let me view the orders that have been received and mark them as shipped.
Enhancing the Model
Adding a Property in the Order.cs File in the SportsStore/Models Folder
This iterative approach of extending and adapting the data model to support different features is typical of ASP.NET Core development. In an ideal world, you would be able to completely define the data model at the start of the project and just build the application around it, but that happens only for the simplest of projects, and, in practice, iterative development is to be expected as the understanding of what is required develops and evolves.
Creating a New Migration
The migration will be applied automatically when the application is started and the SeedData class calls the Migrate method provided by Entity Framework Core.
Displaying Orders to the Administrator
I am going to display two tables, one of which shows the orders waiting to be shipped and the other the shipped orders. Each order will be presented with a button that changes the shipping state. This is not entirely realistic because orders processing is typically more complex than simply updating a field in the database, but integration with warehouse and fulfillment systems is well beyond the scope of this book.
The Contents of the OrderTable.razor File in the SportsStore/Pages/Admin Folder
Razor Components, as the name suggests, rely on the Razor approach to annotated HTML elements. The view part of the component is supported by the statements in the @code section. The @code section in this component defines four properties that are decorated with the Parameter attribute, which means the values will be provided at runtime by the parent component, which I will create shortly. The values provided for the parameters are used in the view section of the component to display details of a sequence of Order objects.
This tells Blazor how to react when the user clicks the button. In this case, the expression tells Razor to call the InvokeAsync method of the OrderSelected property. This is how the table will communicate with the rest of the Blazor application and will become clearer as I build out additional features.
I describe Blazor in-depth in Part 4 of this book, so don’t worry if the Razor Components in this chapter do not make immediate sense. The purpose of the SportsStore example is to show the overall development process, even if individual features are not understood.
The Revised Contents of the Orders.razor File in the SportsStore/Pages/Admin Folder
Blazor Components are not like the other application framework building blocks used for the user-facing sections of the SportsStore application. Instead of dealing with individual requests, components can be long-lived and deal with multiple user interactions over a longer period. This requires a different style of development, especially when it comes to dealing with data using Entity Framework Core. The @inherits expression ensures that this component gets its own repository object, which ensures its operations are separate from those performed by other components displayed to the same user. And to avoid repeatedly querying the database—which can be a serious problem in Blazor, as I explain in Part 4—the repository is used only when the component is initialized, when Blazor invokes the OnInitializedAsync method, or when the user clicks a Refresh Data button.
The values assigned to the OrderTable element’s attributes are used to set the properties decorated with the Parameter attribute in Listing 10-10. In this way, a single component can be configured to present two different sets of data without the need to duplicate code and content.
The ShipOrder and ResetOrder methods are used as the values for the OrderSelected attributes, which means they are invoked when the user clicks one of the buttons presented by the OrderTable component, updating the data in the database through the repository.
Adding Catalog Management
Together, these interfaces allow a user to create, read, update, and delete items in the collection. Collectively, these actions are known as CRUD. In this section, I will implement these interfaces using Blazor.
Developers need to implement CRUD so often that Visual Studio scaffolding includes scenarios for creating CRUD controllers or Razor Pages. But, like all Visual Studio scaffolding, I think it is better to learn how to create these features directly, which is why I demonstrate CRUD operations for all the ASP.NET Core application frameworks in later chapters.
Expanding the Repository
Adding Methods in the IStoreRepository.cs File in the SportsStore/Models Folder
Implementing Methods in the EFStoreRepository.cs File in the SportsStore/Models Folder
Applying Validation Attributes to the Data Model
Adding Validation Attributes in the Product.cs File in the SportsStore/Models Folder
Blazor uses the same approach to validation as the rest of ASP.NET Core but, as you will see, applies it a different way to deal with the more interactive nature of Razor Components.
Creating the List Component
The Revised Contents of the Products.razor File in the SportsStore/Pages/Admin Folder
Creating the Detail Component
The Contents of the Details.razor File in the SportsStore/Pages/Admin Folder
Creating the Editor Component
The Contents of the Editor.razor File in the SportsStore/Pages/Admin Folder
Blazor provides a set of built-in Razor Components that are used to display and validate forms, which is important because the browser can’t submit data using a POST request in a Blazor Component. The EditForm component is used to render a Blazor-friendly form, and the InputText and InputNumber components render input elements that accept string and number values and that automatically update a model property when the user makes a change.
Data validation is integrated into these built-in components, and the OnValidSubmit attribute on the EditForm component is used to specify a method that is invoked only if the data entered into the form conforms to the rules defined by the validation attributes.
Blazor also provides the NavigationManager class, which is used to programmatically navigate between components without triggering a new HTTP request. The Editor component uses NavigationManager, which is obtained as a service, to return to the Products component after the database has been updated.
Deleting Products
Adding Delete Support in the Products.razor File in the SportsStore/Pages/Admin Folder
Summary
In this chapter, I introduced the administration capability and showed you how to use Blazor Server to implement CRUD operations that allow the administrator to create, read, update, and delete products from the repository and mark orders as shipped. In the next chapter, I show you how to secure the administration functions so that they are not available to all users, and I prepare the SportsStore application for deployment into production.