LinkMenuExpand(external link)DocumentSearchCopyCopied

Use with ASP.NET Core Web API

This tutorial will walk through a basic ASP.NET Core JSON API application that performs CRUD operations on blog posts.

Initialize MySQL

Create a MySQL database and copy the following SQL to create a table called BlogPost:

CREATE SCHEMA blog;
USE blog;

CREATE TABLE IF NOT EXISTS `BlogPost` (
  Id INT NOT NULL AUTO_INCREMENT,
  Content LONGTEXT CHARSET utf8mb4,
  Title LONGTEXT CHARSET utf8mb4,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB;

Initialize ASP.NET Core Web API

Create a folder named BlogPostApi, then run dotnet new webapi at the root to create the initial project. Run dotnet add package MySqlConnector.DependencyInjection. You should have a working project at this point, use dotnet run to verify the project builds and runs successfully.

Update Configuration Files

appsettings.json holds .NET Core logging levels and the ADO.NET Connection String:

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
        "Default": "Server=127.0.0.1;User ID=root;Password=pass;Port=3306;Database=blog"
    }
}

.NET Core Startup

Program.cs contains runtime configuration and framework services. Add this call (before var app = builder.Build();) to register a MySQL data source:

builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("Default")!);

Now our app is configured and we can focus on writing the core functionality!

Models

BlogPost.cs is a Plain Old C# Object that represents a single Blog Post.

namespace BlogPostApi;

public class BlogPost
{
    public int Id { get; set; }
    public string? Title { get; set; }
    public string? Content { get; set; }
}

BlogPostRepository.cs contains commands to create, retrieve, update, and delete blog posts from the database:

using System.Data.Common;
using MySqlConnector;

namespace BlogPostApi;

public class BlogPostRepository(MySqlDataSource database)
{
    public async Task<BlogPost?> FindOneAsync(int id)
    {
        using var connection = await database.OpenConnectionAsync();
        using var command = connection.CreateCommand();
        command.CommandText = @"SELECT `Id`, `Title`, `Content` FROM `BlogPost` WHERE `Id` = @id";
        command.Parameters.AddWithValue("@id", id);
        var result = await ReadAllAsync(await command.ExecuteReaderAsync());
        return result.FirstOrDefault();
    }

    public async Task<IReadOnlyList<BlogPost>> LatestPostsAsync()
    {
        using var connection = await database.OpenConnectionAsync();
        using var command = connection.CreateCommand();
        command.CommandText = @"SELECT `Id`, `Title`, `Content` FROM `BlogPost` ORDER BY `Id` DESC LIMIT 10;";
        return await ReadAllAsync(await command.ExecuteReaderAsync());
    }

    public async Task DeleteAllAsync()
    {
        using var connection = await database.OpenConnectionAsync();
        using var command = connection.CreateCommand();
        command.CommandText = @"DELETE FROM `BlogPost`";
        await command.ExecuteNonQueryAsync();
    }

    public async Task InsertAsync(BlogPost blogPost)
    {
        using var connection = await database.OpenConnectionAsync();
        using var command = connection.CreateCommand();
        command.CommandText = @"INSERT INTO `BlogPost` (`Title`, `Content`) VALUES (@title, @content);";
        BindParams(command, blogPost);
        await command.ExecuteNonQueryAsync();
        blogPost.Id = (int)command.LastInsertedId;
    }

    public async Task UpdateAsync(BlogPost blogPost)
    {
        using var connection = await database.OpenConnectionAsync();
        using var command = connection.CreateCommand();
        command.CommandText = @"UPDATE `BlogPost` SET `Title` = @title, `Content` = @content WHERE `Id` = @id;";
        BindParams(command, blogPost);
        BindId(command, blogPost);
        await command.ExecuteNonQueryAsync();
    }

    public async Task DeleteAsync(BlogPost blogPost)
    {
        using var connection = await database.OpenConnectionAsync();
        using var command = connection.CreateCommand();
        command.CommandText = @"DELETE FROM `BlogPost` WHERE `Id` = @id;";
        BindId(command, blogPost);
        await command.ExecuteNonQueryAsync();
    }

    private async Task<IReadOnlyList<BlogPost>> ReadAllAsync(DbDataReader reader)
    {
        var posts = new List<BlogPost>();
        using (reader)
        {
            while (await reader.ReadAsync())
            {
                var post = new BlogPost
                {
                    Id = reader.GetInt32(0),
                    Title = reader.GetString(1),
                    Content = reader.GetString(2),
                };
                posts.Add(post);
            }
        }
        return posts;
    }

    private static void BindId(MySqlCommand cmd, BlogPost blogPost)
    {
        cmd.Parameters.AddWithValue("@id", blogPost.Id);
    }

    private static void BindParams(MySqlCommand cmd, BlogPost blogPost)
    {
        cmd.Parameters.AddWithValue("@title", blogPost.Title);
        cmd.Parameters.AddWithValue("@content", blogPost.Content);
    }
}

Program.cs

Program.cs exposes Async API Endpoints for CRUD operations on Blog Posts (using an ASP.NET Minimal API). Add the following methods before app.Run();:

// GET api/blog
app.MapGet("/api/blog", async ([FromServices] MySqlDataSource db) =>
{
    var repository = new BlogPostRepository(db);
    return await repository.LatestPostsAsync();
});

// GET api/blog/5
app.MapGet("/api/blog/{id}", async ([FromServices] MySqlDataSource db, int id) =>
{
    var repository = new BlogPostRepository(db);
    var result = await repository.FindOneAsync(id);
    return result is null ? Results.NotFound() : Results.Ok(result);
});

// POST api/blog
app.MapPost("/api/blog", async ([FromServices] MySqlDataSource db, [FromBody] BlogPost body) =>
{
    var repository = new BlogPostRepository(db);
    await repository.InsertAsync(body);
    return body;
});

// PUT api/blog/5
app.MapPut("/api/blog/{id}", async (int id, [FromServices] MySqlDataSource db, [FromBody] BlogPost body) =>
{
    var repository = new BlogPostRepository(db);
    var result = await repository.FindOneAsync(id);
    if (result is null)
        return Results.NotFound();
    result.Title = body.Title;
    result.Content = body.Content;
    await repository.UpdateAsync(result);
    return Results.Ok(result);
});

// DELETE api/blog/5
app.MapDelete("/api/blog/{id}", async ([FromServices] MySqlDataSource db, int id) =>
{
    var repository = new BlogPostRepository(db);
    var result = await repository.FindOneAsync(id);
    if (result is null)
        return Results.NotFound();
    await repository.DeleteAsync(result);
    return Results.NoContent();
});

// DELETE api/blog
app.MapDelete("/api/blog", async ([FromServices] MySqlDataSource db) =>
{
    var repository = new BlogPostRepository(db);
    await repository.DeleteAllAsync();
    return Results.NoContent();
});

Run the App

Congratulations, you should have a fully functional app at this point! You should be able to run dotnet run to start your application.

The following API Endpoints should work:

@BlostPostApi_HostAddress = http://localhost:5001

GET {{BlostPostApi_HostAddress}}/api/blog
Accept: application/json
###
POST {{BlostPostApi_HostAddress}}/api/blog
Accept: application/json
Content-Type: application/json

{"title":"test", "content":"test content"}
###
PUT {{BlostPostApi_HostAddress}}/api/blog/1
Accept: application/json
Content-Type: application/json

{"title":"test put", "content":"test content updated"}