Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Note
This isn't the latest version of this article. For the current release, see the .NET 9 version of this article.
Important
This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
For the current release, see the .NET 9 version of this article.
This article is the fourth part of the Blazor movie database app tutorial that teaches you the basics of building an ASP.NET Core Blazor Web App with features to manage a movie database.
This part of the tutorial series focuses on the database context and directly working with the database's schema and data. Seeding the database with data is also covered.
Secure authentication flow required for production apps
This tutorial uses a local database that doesn't require user authentication. Production apps should use the most secure authentication flow available. For more information on authentication for deployed test and production Blazor Web Apps, see the following resources:
- ASP.NET Core Blazor authentication and authorization
- ASP.NET Core Blazor authentication and authorization and the following articles in the Server security node
- Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC)
- Secure an ASP.NET Core Blazor Web App with Microsoft Entra ID
For Microsoft Azure services, we recommend using managed identities. Managed identities securely authenticate to Azure services without storing credentials in app code. For more information, see the following resources:
- What are managed identities for Azure resources? (Microsoft Entra documentation)
- Azure services documentation
Database context
The database context, BlazorWebAppMoviesContext
, connects to the database and maps model objects to database records. The database context was created in the second part of this series. The scaffolded database context code appears in the Program
file:
builder.Services.AddDbContextFactory<BlazorWebAppMoviesContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("BlazorWebAppMoviesContext") ??
throw new InvalidOperationException(
"Connection string 'BlazorWebAppMoviesContext' not found.")));
builder.Services.AddDbContextFactory<BlazorWebAppMoviesContext>(options =>
options.UseSqlite(
builder.Configuration.GetConnectionString("BlazorWebAppMoviesContext") ??
throw new InvalidOperationException(
"Connection string 'BlazorWebAppMoviesContext' not found.")));
builder.Services.AddDbContextFactory<BlazorWebAppMoviesContext>(options =>
options.UseSqlite(
builder.Configuration.GetConnectionString("BlazorWebAppMoviesContext") ??
throw new InvalidOperationException(
"Connection string 'BlazorWebAppMoviesContext' not found.")));
AddDbContextFactory registers a factory for the given context as a service in the app's service collection.
UseSqlServer or UseSqlite configures the context to connect to either a Microsoft SQL Server or SQLite database. Other providers are available to connect to additional types of databases.
GetConnectionString uses the ASP.NET Core Configuration system to read the ConnectionStrings
key for the connection string name provided, which in the preceding example is BlazorWebAppMoviesContext
.
For local development, configuration obtains the database connection string from the app settings file (appsettings.json
). The {CONNECTION STRING}
placeholder in the following example is the connection string:
"ConnectionStrings": {
"BlazorWebAppMoviesContext": "{CONNECTION STRING}"
}
The following is an example connection string:
Server=(localdb)\mssqllocaldb;Database=BlazorWebAppMoviesContext-00001111-aaaa-2222-bbbb-3333cccc4444;Trusted_Connection=True;MultipleActiveResultSets=true
When the app is deployed to a test/staging or production server, securely store the connection string outside of the project's configuration files.
Warning
Don't store app secrets, connection strings, credentials, passwords, personal identification numbers (PINs), private C#/.NET code, or private keys/tokens in client-side code, which is always insecure. In test/staging and production environments, server-side Blazor code and web APIs should use secure authentication flows that avoid maintaining credentials within project code or configuration files. Outside of local development testing, we recommend avoiding the use of environment variables to store sensitive data, as environment variables aren't the most secure approach. For local development testing, the Secret Manager tool is recommended for securing sensitive data. For more information, see Securely maintain sensitive data and credentials.
Database technology
The Visual Studio version of this tutorial uses SQL Server.
SQL Server Express LocalDB is a lightweight version of the SQL Server Express database engine that's targeted for program development. LocalDB starts on demand and runs in user mode, so there's no complex configuration. Master database files (*.mdf
) are placed in the C:/Users/{USER}
directory, where the {USER}
placeholder is the system's user ID.
From the View menu, open SQL Server Object Explorer (SSOX).
Right-click on the Movie
table and select View Designer:
The View Designer opens:
Note the key icon next to ID
. EF creates a property named ID
for the primary key.
Right-click on the Movie
table and select View Data:
The table's data opens in a new tab in Visual Studio:
The VS Code version of this tutorial uses SQLite, which is a public, self-contained, full-featured SQL database engine.
There are many third-party tools you can use to manage and view SQLite databases. The following image shows DB Browser for SQLite:
In this tutorial, EF Core migrations are used. A migration updates the database schema to match changes in the data model. However, migrations can only make changes to the database that the EF Core provider supports. Resources are listed at the end of this article for further reading.
The VS Code version of this tutorial uses SQLite, which is a public, self-contained, full-featured SQL database engine.
There are many third-party tools you can use to manage and view SQLite databases. The following image shows DB Browser for SQLite:
In this tutorial, EF Core migrations are used. A migration updates the database schema to match changes in the data model. However, migrations can only make changes to the database that the EF Core provider supports. Resources are listed at the end of this article for further reading.
Seed the database
Seeding code can create a set of records for development testing or even be used to create the initial data for a new production database.
In the Data
folder, create a new class named SeedData
with the following code.
Data/SeedData.cs
:
using Microsoft.EntityFrameworkCore;
using BlazorWebAppMovies.Models;
namespace BlazorWebAppMovies.Data;
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using var context = new BlazorWebAppMoviesContext(
serviceProvider.GetRequiredService<
DbContextOptions<BlazorWebAppMoviesContext>>());
if (context == null || context.Movie == null)
{
throw new NullReferenceException(
"Null BlazorWebAppMoviesContext or Movie DbSet");
}
if (context.Movie.Any())
{
return;
}
context.Movie.AddRange(
new Movie
{
Title = "Mad Max",
ReleaseDate = new DateOnly(1979, 4, 12),
Genre = "Sci-fi (Cyberpunk)",
Price = 2.51M,
},
new Movie
{
Title = "The Road Warrior",
ReleaseDate = new DateOnly(1981, 12, 24),
Genre = "Sci-fi (Cyberpunk)",
Price = 2.78M,
},
new Movie
{
Title = "Mad Max: Beyond Thunderdome",
ReleaseDate = new DateOnly(1985, 7, 10),
Genre = "Sci-fi (Cyberpunk)",
Price = 3.55M,
},
new Movie
{
Title = "Mad Max: Fury Road",
ReleaseDate = new DateOnly(2015, 5, 15),
Genre = "Sci-fi (Cyberpunk)",
Price = 8.43M,
},
new Movie
{
Title = "Furiosa: A Mad Max Saga",
ReleaseDate = new DateOnly(2024, 5, 24),
Genre = "Sci-fi (Cyberpunk)",
Price = 13.49M,
});
context.SaveChanges();
}
}
A database context instance is obtained from the dependency injection (DI) container. If movies are present, return
is called to avoid seeding the database. When the database is empty, the Mad Max franchise (©Warner Bros. Entertainment) movies are seeded.
To execute the seed initializer, add the following code to the Program
file immediately after the line that builds the app (var app = builder.Build();
). The using
statement ensures that the database context is disposed after the seeding operation completes.
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
SeedData.Initialize(services);
}
If the database contains records from earlier testing, run the app and delete the entities that you created in the database. Stop the app by closing the browser's window.
If the database contains records from earlier testing, run the app and delete the entities that you created in the database. Stop the app by closing the browser's window and pressing Shift+F5 on the keyboard in VS Code.
If the database contains records from earlier testing, run the app and delete the entities that you created in the database. Stop the app by closing the browser's window and pressing Ctrl+C (Windows) in the command shell.
When the database is empty, run the app.
Navigate to the movies Index
page to see the seeded movies:
Bind a form to a model
Review the Edit
component (Components/Pages/MoviePages/Edit.razor
).
When an HTTP GET request is made for the Edit
component page (for example at the relative URL: /movies/edit?id=6
):
- The OnInitializedAsync method fetches the movie with an
Id
of6
from the database and assigns it to theMovie
property. - The EditForm.Model parameter specifies the top-level model object for the form. An edit context is constructed for the form using the assigned model.
- The form is displayed with the values from the movie.
When the Edit
page is posted to the server, the form values on the page are bound to the Movie
property because the [SupplyParameterFromForm]
attribute is annotated on the Movie
property:
[SupplyParameterFromForm]
private Movie? Movie { get; set; }
If the model state has errors when the form is posted, for example if ReleaseDate
can't be converted into a date, the form is redisplayed with the submitted values. If no model errors exist, the movie is saved using the form's posted values.
Concurrency exception handling
Review the UpdateMovie
method of the Edit
component (Components/Pages/MoviePages/Edit.razor
):
private async Task UpdateMovie()
{
using var context = DbFactory.CreateDbContext();
context.Attach(Movie!).State = EntityState.Modified;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie!.Id))
{
NavigationManager.NavigateTo("notfound");
}
else
{
throw;
}
}
NavigationManager.NavigateTo("/movies");
}
Concurrency exceptions are detected when one client deletes the movie and a different client posts changes to the movie.
To test how concurrency is handled by the preceding code:
- Select Edit for a movie, make changes, but don't select Save.
- In a different browser window, open the app to the movie
Index
page and select the Delete link for the same movie to delete the movie. - In the previous browser window, post changes to the movie by selecting the Save button.
- The browser is navigated to the
notfound
endpoint, which doesn't exist and yields a 404 (Not Found) result.
Additional guidance on handling concurrency with EF Core in Blazor apps is available in the Blazor documentation.
Stop the app
If the app is running, shut the app down by closing the browser's window.
If the app is running, shut the app down by closing the browser's window and pressing Shift+F5 on the keyboard in VS Code.
If the app is running, shut the app down by closing the browser's window and pressing Ctrl+C in the command shell.
Troubleshoot with the completed sample
If you run into a problem while following the tutorial that you can't resolve from the text, compare your code to the completed project in the Blazor samples repository:
Blazor samples GitHub repository (dotnet/blazor-samples
)
Select the latest version folder. The sample folder for this tutorial's project is named BlazorWebAppMovies
.
Additional resources
- Configuration articles:
- Configuration in ASP.NET Core (ASP.NET Core Configuration system)
- ASP.NET Core Blazor configuration (Blazor documentation)
- Data seeding (EF Core documentation)
- Concurrency with EF Core in Blazor apps
- Database provider resources:
- Blazor Web App security
- ASP.NET Core Blazor authentication and authorization
- ASP.NET Core Blazor authentication and authorization and the following articles in the Server security node
- Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC)
- Secure an ASP.NET Core Blazor Web App with Microsoft Entra ID
Legal
Mad Max, The Road Warrior, Mad Max: Beyond Thunderdome, Mad Max: Fury Road, and Furiosa: A Mad Max Saga are trademarks and copyrights of Warner Bros. Entertainment.
Next steps
ASP.NET Core