This guide provides a detailed step-by-step walkthrough for creating an ASP.NET Core Blazor Web App that performs full CRUD (Create, Read, Update, Delete) operations on a MySQL database. We will build the application progressively, with each section representing a complete, functional stage of development.

Why dotConnect for MySQL?

ORM support provided

Advanced ORM support

Fully supports EF Core, Dapper, NHibernate, LinqConnect, and other technologies for efficient data management.

Full ADO.NET compliance

Full ADO.NET compliance

Conforms to the latest ADO.NET standards and innovations for seamless integration with .NET applications.

Support for MySQL-specific data types

MySQL-specific data types

Offers many MySQL-specific features and fully supports all unique data types for accurate and complete data representation.

Secure connection ensured

Secure connection options

Provides robust security with support for SSL/SSH connections, connecting via proxy servers, embedded servers, and HTTP tunneling.

Integration with popular IDEs

IDE integration

Features native integration with Visual Studio and complete design-time support for accelerated development.

Priority support provided

Priority support & frequent updates

Includes priority support, detailed documentation, and regular updates for continuous improvement.

Download and activate dotConnect for MySQL

You can start using dotConnect for MySQL immediately with a 30-day free trial. Choose one of the following installation options:

30-day free trial version

dotnet add package Devart.Data.MySql
Install-Package Devart.Data.MySql

You can install the driver by using the Windows installer.

After you receive the license key, add it to your connection strings to connect to the data source.

Start using dotConnect for MySQL in your project today with a free trial

Check MySQL database objects

To connect to MySQL using the built-in Data Explorer, click Tools and select Connect to Database.

Connect to a MySQL database

Choose MySQL as the data source, enter your server details and credentials, and click Connect.

Connection established successfully

Once connected, you can browse tables, execute queries, and manage data directly within Data Explorer.

Data Explorer displaying MySQL tables

Connection setup

Open the appsettings.json file and add your MySQL connection string:

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=127.0.0.1;Port=3306;Database=sakila;User Id=TestUser;Password=TestPassword;License key=**********"
  },
  "Logging": {
    // ...
  }
}

Connect and retrieve data

In this foundational step, our goal is to establish a connection to the MySQL database and display data in a read-only table. This ensures our database connection and data service are correctly configured and provides the base upon which we will build the other CRUD operations.

Data model (Actor.cs)

In your project, create a new folder named Data. Inside it, create a class file named Actor.cs. This class will act as a model to hold the data for each actor record.

// Data/Actor.cs
namespace MySqlBlazor.Data;

public class Actor
{
    public int ActorId { get; set; }
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public DateTime LastUpdate { get; set; }
}

Data service (ActorService.cs)

In the Data folder, create a new class ActorService.cs. This service will contain the logic to interact with the database. Note that we will use Dependency Injection to provide this service to our components, so the class is not static.

// Data/ActorService.cs
using Devart.Data.MySql;
using System.Collections.Generic;

namespace MySqlBlazor.Data;

public class ActorService
{
    private readonly string _connectionString;

    public ActorService(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("DefaultConnection")!;
    }

    public async Task<List<Actor>> GetAllActorsAsync()
    {
        var actors = new List<Actor>();
        using var connection = new MySqlConnection(_connectionString);
        await connection.OpenAsync();
        using var command = new MySqlCommand("SELECT actor_id, first_name, last_name, last_update FROM actor ORDER BY actor_id", connection);
        using var reader = await command.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            actors.Add(new Actor
            {
                ActorId = reader.GetInt32(reader.GetOrdinal("actor_id")),
                FirstName = reader.GetString(reader.GetOrdinal("first_name")),
                LastName = reader.GetString(reader.GetOrdinal("last_name")),
                LastUpdate = reader.GetDateTime(reader.GetOrdinal("last_update"))
            });
        }
        return actors;
    }
}

Registering the service

In Program.cs, register the ActorService as a scoped service so it can be injected into our components.

// Program.cs
using MySqlBlazor.Data;

// ... other using statements
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

// Register the ActorService
builder.Services.AddScoped<ActorService>();

var app = builder.Build();
// ...

Actors page (Actors.razor)

In the Components/Pages folder, create a new Razor component named Actors.razor. This page will fetch and display the data.

@page "/actors"
@rendermode InteractiveServer
@using MySqlBlazor.Data
@inject ActorService ActorSvc

<PageTitle>Actors</PageTitle>

<h1>Actor List</h1>

@if (actors == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Last Update</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var actor in actors)
            {
                <tr>
                    <td>@actor.ActorId</td>
                    <td>@actor.FirstName</td>
                    <td>@actor.LastName</td>
                    <td>@actor.LastUpdate.ToString("yyyy-MM-dd HH:mm")</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private List<Actor>? actors;

    protected override async Task OnInitializedAsync()
    {
        actors = await ActorSvc.GetAllActorsAsync();
    }
}

Add a navigation link

In Components/Layout/NavMenu.razor, add a link to your new page.

<div class="nav-item px-3">
    <NavLink class="nav-link" href="actors">
        <span class="bi bi-person-fill-gear" aria-hidden="true"></span> Actors
    </NavLink>
</div>

You have now built a functional, read-only Blazor application that connects to a MySQL database and displays the data in a table. This confirms that your core setup, including dependency injection and data retrieval, is working correctly.

Read-only Blazor application

Insert data

Now we will enhance the application by adding the ability to create new records. This involves adding an EditForm to the UI and implementing the corresponding logic to execute an INSERT SQL command.

Update the UI (Actors.razor)

Modify Actors.razor to add a dedicated EditForm for creating new actors.

@page "/actors"
@rendermode InteractiveServer
@using MySqlBlazor.Data
@inject ActorService ActorSvc
@inject IJSRuntime JSRuntime // Add this for delete functionality later

<PageTitle>Actors</PageTitle>

<h1>Actors</h1>

<h3>Add New Actor</h3>
<EditForm Model="@newActor" OnValidSubmit="HandleInsert" FormName="NewActorForm">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="mb-3">
        <label for="newFirstName" class="form-label">First Name</label>
        <InputText id="newFirstName" @bind-Value="newActor.FirstName" class="form-control" />
    </div>
    <div class="mb-3">
        <label for="newLastName" class="form-label">Last Name</label>
        <InputText id="newLastName" @bind-Value="newActor.LastName" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">Insert Actor</button>
</EditForm>
<hr />
Read-only Blazor application

Update the data service (ActorService.cs)

Add the InsertActorAsync method to your ActorService.cs file.

// Data/ActorService.cs
// ... (add this async method inside the ActorService class)
    public async Task InsertActorAsync(Actor actor)
    {
        using var connection = new MySqlConnection(_connectionString);
        await connection.OpenAsync();
        using var command = new MySqlCommand("INSERT INTO actor (first_name, last_name, last_update) VALUES (:first_name, :last_name, NOW())", connection);
        command.Parameters.AddWithValue(":first_name", actor.FirstName);
        command.Parameters.AddWithValue(":last_name", actor.LastName);
        await command.ExecuteNonQueryAsync();
    }

Update the UI logic (Actors.razor)

Modify the @code block in Actors.razor to handle the form submission for adding new actors.

@code {
    private List<Actor>? actors;
    private Actor newActor = new(); // Object to hold new actor data for adding
    private Actor actorToEdit = new(); // Object to hold actor data for editing (initialized later)

    protected override async Task OnInitializedAsync()
    {
        await LoadActors();
    }

    private async Task LoadActors()
    {
        actors = await ActorSvc.GetAllActorsAsync();
    }

    private async Task HandleInsert()
    {
        await ActorSvc.InsertActorAsync(newActor);
        newActor = new(); // Clear the form after insert
        await LoadActors(); // Refresh the list
    }

    // HandleUpdate, SetActorForUpdate, and ClearForm methods will be added/modified in Step 3
}
Data inserted into the Blazor application

The application can now add new records to the database. Users can input data into the form, press the Insert Actor button, and see the table update immediately with the new entry.

Blazor app supporting data insertion

Update data

In this step, we will implement the ability to edit existing records. This requires adding Edit buttons to our table and logic to populate the form for editing. We will reuse the same EditForm.

Update the UI (Actors.razor)

Modify Actors.razor to include a conditional EditForm for updating actors and add Edit and Delete buttons to each row in the table.

<!-- ... (Existing 'Add New Actor' form and <hr />) ... -->

@if (actorToEdit.ActorId != 0)
{
    <h3>Edit Actor</h3>
    <EditForm Model="@actorToEdit" OnValidSubmit="HandleUpdate" FormName="EditActorForm">
        <DataAnnotationsValidator />
        <ValidationSummary />

        <div class="mb-3">
            <label for="editFirstName" class="form-label">First Name</label>
            <InputText id="editFirstName" @bind-Value="actorToEdit.FirstName" class="form-control" />
        </div>
        <div class="mb-3">
            <label for="editLastName" class="form-label">Last Name</label>
            <InputText id="editLastName" @bind-Value="actorToEdit.LastName" class="form-control" />
        </div>
        <button type="submit" class="btn btn-success">Save</button>
        <button type="button" class="btn btn-secondary" @onclick="ClearForm">Cancel</button>
    </EditForm>
    <hr />
}

@if (actors == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Last Update</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (var actor in actors)
            {
                <tr>
                    <td>@actor.ActorId</td>
                    <td>@actor.FirstName</td>
                    <td>@actor.LastName</td>
                    <td>@actor.LastUpdate.ToString("yyyy-MM-dd HH:mm")</td>
                    <td>
                        <button class="btn btn-sm btn-warning" @onclick="() => SetActorForUpdate(actor)">Edit</button>
                        <button class="btn btn-sm btn-danger" @onclick="() => HandleDelete(actor)">Delete</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>
}
Blazor application with data editing options

Update the data service (ActorService.cs)

Add the UpdateActorAsync method to ActorService.cs.

// Data/ActorService.cs
// ... (add this async method inside the ActorService class)
    public async Task UpdateActorAsync(Actor actor)
    {
        using var connection = new MySqlConnection(_connectionString);
        await connection.OpenAsync();
        using var command = new MySqlCommand("UPDATE actor SET first_name = :first_name, last_name = :last_name, last_update = NOW() WHERE actor_id = :actor_id", connection);
        command.Parameters.AddWithValue(":first_name", actor.FirstName);
        command.Parameters.AddWithValue(":last_name", actor.LastName);
        command.Parameters.AddWithValue(":actor_id", actor.ActorId);
        await command.ExecuteNonQueryAsync();
    }

Update the UI logic (Actors.razor)

Update the @code block in Actors.razor to manage the state for both inserting and updating.

@code {
    private List<Actor>? actors;
    private Actor newActor = new(); // For new actor input
    private Actor actorToEdit = new(); // For editing existing actors

    protected override async Task OnInitializedAsync()
    {
        await LoadActors();
    }

    private async Task LoadActors()
    {
        actors = await ActorSvc.GetAllActorsAsync();
    }

    private async Task HandleInsert()
    {
        await ActorSvc.InsertActorAsync(newActor);
        newActor = new(); // Clear the form after insert
        await LoadActors();
    }

    private void SetActorForUpdate(Actor actor)
    {
        // Create a copy to avoid modifying the list directly
        actorToEdit = new Actor
        {
            ActorId = actor.ActorId,
            FirstName = actor.FirstName,
            LastName = actor.LastName
        };
    }

    private async Task HandleUpdate()
    {
        await ActorSvc.UpdateActorAsync(actorToEdit);
        ClearForm();
        await LoadActors(); // Refresh the list
    }

    private void ClearForm()
    {
        actorToEdit = new(); // Reset actorToEdit for next edit/add
    }

    // HandleDelete method will be added in Step 4
}
Data editing in a Blazor application

The application now supports editing data. Users can click Edit on an actor, change their details in the form, and save the changes to the database.

Blazor application with both data editing and data insertion options

Delete data

In the final step, we will implement the delete functionality. This involves adding a Delete button and using Blazor's JavaScript Interop feature to show a confirmation dialog.

Update the UI (Actors.razor)

Add a Delete button to each row in the actor table.

<!-- ... (Inside the table's foreach loop) ... -->
<td>
    <button class="btn btn-sm btn-warning" @onclick="() => SetActorForUpdate(actor)">Edit</button>
    <button class="btn btn-sm btn-danger" @onclick="() => HandleDelete(actor)">Delete</button>
</td>
Blazor application with data deletion options

Update the data service (ActorService.cs)

Add the DeleteActorAsync method to ActorService.cs.

// Data/ActorService.cs
// ... (add this async method inside the ActorService class)
    public async Task DeleteActorAsync(int actorId)
    {
        using var connection = new MySqlConnection(_connectionString);
        await connection.OpenAsync();
        using var command = new MySqlCommand("DELETE FROM actor WHERE actor_id = :actor_id", connection);
        command.Parameters.AddWithValue(":actor_id", actorId);
        await command.ExecuteNonQueryAsync();
    }

Update the UI Logic (Actors.razor)

Inject the IJSRuntime service and add the HandleDelete method to the @code block. This method will use JavaScript to ask the user for confirmation before deleting.

@code {
    private List<Actor>? actors;
    private Actor newActor = new(); // For new actor input
    private Actor actorToEdit = new(); // For editing existing actors

    protected override async Task OnInitializedAsync()
    {
        await LoadActors();
    }

    private async Task LoadActors()
    {
        actors = await ActorSvc.GetAllActorsAsync();
    }

    private async Task HandleInsert()
    {
        await ActorSvc.InsertActorAsync(newActor);
        newActor = new(); // Clear the form after insert
        await LoadActors();
    }

    private void SetActorForUpdate(Actor actor)
    {
        // Create a copy to avoid modifying the list directly
        actorToEdit = new Actor
        {
            ActorId = actor.ActorId,
            FirstName = actor.FirstName,
            LastName = actor.LastName
        };
    }

    private async Task HandleUpdate()
    {
        await ActorSvc.UpdateActorAsync(actorToEdit);
        ClearForm();
        await LoadActors(); // Refresh the list
    }

    private void ClearForm()
    {
        actorToEdit = new(); // Reset actorToEdit for next edit/add
    }

    private async Task HandleDelete(Actor actor)
    {
        bool confirmed = await JSRuntime.InvokeAsync<bool>("confirm", $"Are you sure you want to delete {actor.FirstName} {actor.LastName}?");
        if (confirmed)
        {
            await ActorSvc.DeleteActorAsync(actor.ActorId);
            await LoadActors(); // Refresh the list
        }
    }
}
Data deletion confirmation

You have now completed the Blazor application. It has full CRUD functionality, allowing users to read, create, update, and delete records from the MySQL database in a modern web interface.

Blazor application with all data management options

Video tutorial: How to Connect a .NET Blazor Application to a MySQL Database

Conclusion

Now you know how to connect a Blazor application to a MySQL database using dotConnect for MySQL. You know how to create new tables, insert data into them, read, update, and delete records. Which means you are all set to try dotConnect for MySQL in your real-life project. Simply download dotConnect for MySQL for a free 30-day trial and give it a go today!

FAQ

How do you install and activate dotConnect for MySQL in a .NET project?
Install dotConnect for MySQL via the EXE installer or by adding the Devart.Data.MySql NuGet package to your project, then obtain your personal activation key from your Devart Customer Portal and include it in the connection string via the License key parameter for a working connection.
How do you create a connection to MySQL using dotConnect in C#?
Define a connection string that includes host, user ID, password, database, and the License key value, then create a MySqlConnection instance with this string and call Open() for it inside a try-catch block to test and handle connection errors.
How do you enable SSL/TLS for secure MySQL connections with dotConnect?
Add Protocol=SSL and specify the SSL CA Cert, SSL Cert, and SSL Key file paths in the connection string, then open the MySqlConnection connection to establish an encrypted SSL/TLS connection.
Can you connect to MySQL using Entity Framework Core and dotConnect?
Yes, you can either use Entity Developer to visually create an EF Core model from the database or run Scaffold-DbContext with a dotConnect connection string (including the License key) to generate the DbContext and entity classes.
Is it possible to connect to MySQL using Visual Studio Server Explorer with dotConnect?
Yes, in Visual Studio Server Explorer, you add a new Data Connection, choose dotConnect for MySQL as the data provider, enter your MySQL server credentials, test the connection, and then browse and manage data directly from the IDE.

Dereck Mushingairi

I'm a technical content writer who loves turning complex topics — think SQL, connectors, and backend chaos–into content that actually makes sense (and maybe even makes you smile). I write for devs, data folks, and curious minds who want less fluff and more clarity. When I'm not wrangling words, you'll find me dancing salsa, or hopping between cities.

dotConnect for MySQL

Get an enhanced ORM-enabled data provider for MySQL and develop .NET applications working with MySQL data quickly and easily!

Try the 30-day trial of the full product. No limits. No card required. Start free trial