Blazor Boilerplate: Release 2.0.0
Blazor Boilerplate: Release 2.0.0
Release 2.0.0
13.03.2021
Introduction
1 Blazor Server 3
1.1 Pros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Cons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Blazor WebAssembly 5
2.1 Pros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Cons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2.1 Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.2 Breeze Sharp with Blazor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.3 Entity Framework Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.4 Localization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2.5 Deploy with Terraform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.6 Dual mode Blazor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.7 MultiTenancy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2.8 IdentityServer4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
i
ii
Blazor Boilerplate, Release 2.0.0
Introduction 1
Blazor Boilerplate, Release 2.0.0
2 Introduction
KAPITEL 1
Blazor Server
1.1 Pros
• Download size is significantly smaller than a Blazor WebAssembly app, and the app loads much faster.
• The app takes full advantage of server capabilities, including use of any .NET Core compatible APIs.
• .NET Core on the server is used to run the app, so existing .NET tooling, such as debugging, works as expected.
• Thin clients are supported. For example, Blazor Server apps work with browsers that don’t support WebAssem-
bly and on resource-constrained devices.
• The app’s .NET/C# code base, including the app’s component code, isn’t served to clients.
1.2 Cons
• Higher latency usually exists. Every user interaction involves a network hop.
• There’s no offline support. If the client connection fails, the app stops working.
• Scalability is challenging for apps with many users. The server must manage multiple client connections and
handle client state.
• An ASP.NET Core server is required to serve the app. Serverless deployment scenarios aren’t possible (for
example, serving the app from a CDN).
3
Blazor Boilerplate, Release 2.0.0
Blazor WebAssembly
2.1 Pros
• There’s no .NET server-side dependency. The app is fully functioning after it’s downloaded to the client.
• Client resources and capabilities are fully leveraged.
• Work is offloaded from the server to the client.
• An ASP.NET Core web server isn’t required to host the app. Serverless deployment scenarios are possible (for
example, serving the app from a CDN).
2.2 Cons
5
Blazor Boilerplate, Release 2.0.0
2.2.1 Architecture
The diagram shows the dependencies of the main projects. Every project with text to localize depends on Localization
project.
Client project is used only with Blazor WebAssembly, so it runs on the browser. It initializes the WebAssembly startup.
Server project manages the main services: authentication, authorization, API, etc.
Storage project manages the persistence of models in a database with Entity Framework Core.
Infrastructure project contains interfaces and models in common among projects running on server.
Shared project contains base interfaces and models in common among projects running on server or client.
Module projects contains UI, services, etc. that define your web application.
Bemerkung: What you will read here are my personal opinions. I have been using Breeze from years for both web
and desktop applications and I have no connection with Breeze team.
Giovanni Quarella
2.2. Cons 7
Blazor Boilerplate, Release 2.0.0
Do you know good coders are lazy? Do you know the DRY principle?
The solution is a Breeze Controller.
As you can read in the official documentation: “One controller to rule them all . . . ”.
Breeze Server
In this project the Breeze Controller is ApplicationController and the EFPersistenceManager is ApplicationPersis-
tenceManager.
The ApplicationController has no Authorize attribute, because to keep policy management with Breeze, I implemented
a simple custom policy management in ApplicationPersistenceManager.
First of all you can mark the EF entities with Permissions attribute which takes CRUD Actions flag enum as parameter.
Examples:
[Permissions(Actions.Delete)]
public partial class Todo
{
...
}
[Permissions(Actions.Create | Actions.Update)]
public partial class Todo
{
...
}
[Permissions(Actions.CRUD)]
public partial class Todo
{
...
}
In BeforeSaveEntities method of ApplicationPersistenceManager the actions of the entities to be saved are compared
with the claims of permission type of the current user. If a policy is violated an EntityErrorsException is thrown.
To check Actions.Read in ApplicationController you have to access EF DbSet with GetEntities method of Applica-
tionPersistenceManager.
To access Breeze Controller you have to use Breeze.Sharp. In Blazor BlazorBoilerplate ApiClient is what you need to
query ApplicationController. You can inject IApiClient in every Razor page where entities are requested.
The Breeze.Sharp entities are the so called DTO and you have create one for every entities of your Entity Framework
context. In this project they are in BlazorBoilerplate.Shared.Dto.Db.
Notes
At the moment Todo is the only entity used to demonstrate Breeze.Sharp capabilities. In fact you can extend the use
to all other entities, but administrative entities (users, roles, etc.) are managed by others libraries like ASP.NET Core
Identity and IdentityServer4, so I preferred to keep the dedicated AdminController.
If you think that is all wonderful, here it comes the drawback.
The used of C# as a replacement of javascript is something very new: it is the Blazor revolution. So till now Bree-
ze.Sharp has been used a little only on desktop application. In fact the most used Breeze client is the javascript client
BreezeJS and Breeze itself is not .NET centric.
For this reason breeze.sharp library is rarely updated, so to make BlazorBoilerplate working with Breeze I used my
fork GioviQ/breeze.sharp where I fixed some issues. You can find the package in folder nupkg and nuget.config tells
Visual Studio to use this folder as a package source.
Migrations
You can perform Entity Framework Core migrations directly from Visual Studio with Package Manager Console or
with command-line interface.
Here some tips to manage migrations in Blazor Boilerplate with command-line interface (CLI).
1. Make sure you have installed CLI; from the command line execute:
or to update
Without the –no-build parameter, dotnet rebuilds the startup project, but if you have just built it with Visual Studio, it
is just a waste of time. Also the dotnet build is not the Visual Studio build, so to avoid issue, use this procedure.
The name of migration, in this case 4Preview3, is only descriptive and it does not need to be unique, because automa-
tically the name is prepended with date time.
If the command is successful, [datetime]_[migration-name].cs is added to the solution. Also the [db-context-
name]ModelSnapshot.cs is updated to reflect the db changes in case a new db has to be created.
When you run the project, the migrations are applied to the database (in our case we have only one db). The db table
__EFMigrationsHistory keeps track of applied migrations without information about the related db context. To get
this information use the following command; for example for ConfigurationDbContext:
You can also update the database, without running the project with the following command:
2.2. Cons 9
Blazor Boilerplate, Release 2.0.0
If you specify a previous migration, you can revert the db changes to that migration.
Warning
The migrations are not smart, when you have an existing db with populated tables. If migrations add keys and/or
unique indexes or something else violating referential integrity, you have to manually modify [datetime]_[migration-
name].cs for example to add some SQL to fix the errors. E.g. migrationBuilder.Sql(„UPDATE AspNetUserLogins
SET Id=NEWID() WHERE Id=““); to add unique values to a new field before setting as a new primary key.
Always keeping in mind the DRY principle, it is boring implementing audit information and adding the same properties
CreatedOn, ModifiedOn, CreatedBy and ModifiedBy to all entities.
Some articles teach you to use Shadow Properties to add audit information, but this is not the right solution, if you
want expose these properties on the mapped entity types and use them e.g. in UI.
A solution is using Source Generator. AuditableGenerator generates for every class implementing IAuditable the
above properties. Remember all classes implementing IAuditable have to be partial.
2.2.4 Localization
Localization has moved from Resource based localization to Database based localization, so translation are no more
in dll satellite libraries, but in table LocalizationRecords. Look at LocalizationDbContext.
The localization code is in BlazorBoilerplate.Shared.Localizer project. The supported cultures are defined in Set-
tings.cs.
At this time Data Annotations do not support IStringLocalizer<>, so to localize validation error messages, we have
to use Blazored.FluentValidation.
Po files
This project adopts the PO file format as standard translations files. In the admin section you can edit translations and
import and export PO files. There are a lot of free and paid tools and online services to manage these files. E.g.:
Poedit
Eazy Po
Crowdin (PO file format support)
To manage PO files BlazorBoilerplate uses Karambolo.PO library. So to handle plurals the Karambolo.PO syntax is
used like in the example below.
Everywhere you need localized text, inject IStringLocalizer<Global> and look how it is used throughout the code.
Terraform Overview
• Use terraform to deploy to nearly any hosting environment
• Terraform is open source https://github.com/hashicorp/terraform
• For more information go to https://www.terraform.io/
Pass
1. Once pass is installed and initialized do the following to store the environment variables in pass
2.2. Cons 11
Blazor Boilerplate, Release 2.0.0
cd ./src/Utils/Terraform/AWS
terraform init
terraform plan
terraform apply
1. Once pass is installed and initialized do the following to store the environment variables in pass
todo:
pass insert param1
pass insert param2
todo:
export TF_VAR_param1=$(pass param1)
export TF_VAR_param2=$(pass param2)
cd ./src/Utils/Terraform/Azure
terraform init
terraform plan
terraform apply
As stated in doc home Blazor has two hosting models: WebAssembly or Server. Each hosting model has pros and
cons, so you have to decide which is better for your web application.
With Blazor Boilerplate you can switch at runtime between WebAssembly or Server in the Settings of Admin.
Choose the desiderd runtime in the dropdown list and then click on Save button. The application is then reloaded.
2.2.7 MultiTenancy
services.AddMultiTenant()
.WithHostStrategy("__tenant__")
.WithEFCoreStore<TenantStoreDbContext>()
.WithFallbackStrategy(Settings.DefaultTenantId);
</application>
<bindings>
<binding protocol="http" bindingInformation="*:53414:localhost" />
<binding protocol="http" bindingInformation="*:53414:tenant1.local" />
<binding protocol="http" bindingInformation="*:53414:tenant2.local" />
</bindings>
</site>
2.2. Cons 13
Blazor Boilerplate, Release 2.0.0
Delete the previous database if any, because DatabaseInitializer inits TenantInfo for the two tenants.
Run debug and in Admin UI you will find a MultiTenancy section with links to the two demo tenants.
In the following screenshot you see the configuration with the two online demo tenants.
2.2.8 IdentityServer4
[Route("api/data/[action]")]
[Authorize(AuthenticationSchemes = AuthSchemes)]
[BreezeQueryFilter]
public class ApplicationController : Controller
{
private const string AuthSchemes =
"Identity.Application" + "," + IdentityServerAuthenticationDefaults.
˓→AuthenticationScheme; //Cookie + Token authentication
In fact as you can see ApplicationController uses both cookie and bearer token authentication scheme.
Currently ApiClient uses cookie authentication to access ApplicationController. To see an example of external access
with ApiClient and bearer authentication, you have to look at BlazorBoilerplate.Server.Tests project.