目次
とある日。。。
天井を見ながらこう思った。「あ~C#でMySQL使いてぇ~。」
というわけで
作っていこうと思う。対象者は、C#でMySQLをイジメたい人向けです。
環境
- VSCode with C#(.NET) extension
- .NET 8.0.11 SDK
- Docker
プロジェクト作成
以下のコマンドでプロジェクトを作成する
E:\Project> dotnet new webapi -o DatabaseApi
E:\Project> cd DatabaseApi
E:\Project\DatabaseApi> dotnet new sln
E:\Project\DatabaseApi> dotnet sln add .
NuGetパッケージの追加
MySQLをC#上で使えるようにするため、パッケージを追加する
E:\Project\DatabaseApi> dotnet add package MySql.EntityFrameworkCore --version 8.0.8
書いていこう
テーブル操作を統一するために、リポジトリインターフェースを実装する
お好みに改造してかまわない
namespace DatabaseApi;
///<summary>
/// テーブル操作インターフェース
/// <p>
/// T - テーブルデータクラス
/// </p>
///</summary>
public interface IRepository<T> where T : class
{
protected Task<T> CreateAsync(T newData);
protected Task<T> UpdateAsync(T updatedData);
protected Task<bool> DeleteAsync(T targetData);
}
データベース操作用の基底コンテキストクラスを作成する
using Microsoft.EntityFrameworkCore;
namespace DatabaseApi;
public abstract partial class BaseDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
throw new NotImplementedException();
}
}
データベース用のコンテキストクラスを作成する
#pragma warning disable CS8618
using Microsoft.EntityFrameworkCore;
using MySql.Data.MySqlClient;
namespace DatabaseApi;
public partial class ExampleDbContext() : BaseDbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionStr = new MySqlConnectionStringBuilder()
{
Server = "localhost",
Port = 3306,
UserID = "root",
Password = "P@ssw0rd",
Database = "Example"
}.ToString();
optionsBuilder.UseMySQL(connectionStr);
}
}
テーブル用のデータオブジェクトクラスを追加する
using System.ComponentModel.DataAnnotations;
namespace DatabaseApi;
public class TableA
{
[Key]
public string Id {get; set;} = null!;
public string Name {get; set;} = string.Empty;
public string Birthday {get; set;} = string.Empty;
}
データベース用のコンテキストクラスにテーブルを追加する
#pragma warning disable CS8618
using Microsoft.EntityFrameworkCore;
using MySql.Data.MySqlClient;
namespace DatabaseApi;
public partial class ExampleDbContext() : BaseDbContext, IRepository<TableA>
{
public DbSet<TableA> Users {get; private set;}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
...
}
public async Task<TableA> CreateAsync(TableA newData) { ... }
public async Task<TableA> UpdateAsync(TableA updatedData) { ... }
public async Task<TableA> DeleteAsync(TableA targetData) { ... }
}
同期関数でも大丈夫ですが、同時にいくつも処理を行う場合は非同期にしてください。
処理を追加する
public async Task<TableA> CreateAsync(TableA newData)
{
await Users.AddAsync(newData);
await SaveChangesAsync();
return newData;
}
public async Task<TableA> UpdateAsync(TableA updatedData)
{
var target = await Users.FirstOrDefaultAsync(x => x.Id == updatedData.Id);
if (target == null) throw new Exception("Target not found.");
target.Name = updatedData.Name;
target.Birthday = updatedData.Birthday;
Users.Update(target);
await SaveChangesAsync();
return target;
}
public async Task<TableA> DeleteAsync(TableA targetData)
{
var target = await Users.FirstOrDefaultAsync(x => x.Id == targetData.Id);
if (target == null) throw new Exception("Target not found.");
try {
Users.Remove(target);
await SaveChangesAsync();
return true;
} catch {
return false;
}
}
webapiを書き換える
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(new DatabaseApi.ExampleDbContext());
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.MapGet("/get", async (DatabaseApi.ExampleDbContext db) =>
{
return await db.Users.ToListAsync();
});
app.MapPost("/add", async (DatabaseApi.ExampleDbContext db) =>
{
var newData = new DatabaseApi.TableA()
{
Id = "1",
Name = "John",
Birthday = DateTime.Now.ToString()
};
return await db.CreateAsync(newData);
});
app.MapPost("/update", async (DatabaseApi.ExampleDbContext db) =>
{
var newData = new DatabaseApi.TableA()
{
Id = "1",
Name = "Josh",
Birthday = DateTime.Now.ToString()
};
return await db.UpdateAsync(newData);
});
app.MapDelete("/remove", async (DatabaseApi.ExampleDbContext db) =>
{
var newData = new DatabaseApi.TableA()
{
Id = "1"
};
return await db.DeleteAsync(newData);
});
app.Run();
これで、一通りの処理を追加したのでこれで使えるはず。
、、、DB無くね?
DockerコンテナでMySQLを起動する
以下のファイルを、プロジェクトフォルダの.containerフォルダ(ないと思うので作ってください)に作成する
FROM mysql:latest
# この書き方は望ましくない(非推奨)ので心配なら別の方法をググってください
ENV TZ="Asia/Tokyo"
ENV MYSQL_ROOT_PASSWORD="P@ssw0rd"
ENV MYSQL_DATABASE="Example"
EXPOSE 3306
COPY ./init.sql /docker-entrypoint-initdb.d
USE Example;
CREATE TABLE IF NOT EXISTS Users (
Id VARCHAR(255) NOT NULL PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
Birthday VARCHAR(255)
);
E:\Project> docker build --pull --rm -f ".container\MySql.Dockerfile" -t mysql:latest ".container"
E:\Project> docker run -itd -p 3306:3306 mysql
成果物
VSCodeの拡張機能のThunder Client
などを使用して以下の順序でリクエストを送信してみてください。すると、データを操作できていることがわかるはずです。もし、MySQLの設定が違ったり、データベースやテーブルの構造が異なったりする場合は、例外が発生します。
- http://localhost:5180/add
- http://localhost:5180/get
- http://localhost:5180/update
- http://localhost:5180/remove
- http://localhost:5180/get
ポート番号はプロジェクトの生成時に決定するため、人それぞれです。起動ログから確認できます。
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5180 <-------------- ココ!!
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: E:\Project\DatabaseApi
終わりに
これでC#を使って、MySQLを利用できるようになりましたね!
改善点や修正点などあれば、随時、編集リクエスト、コメントお待ちしております。
追記
このMySql.EntityFrameworkCore
では、以下の脆弱性が警告として出ます。
warning NU1903: パッケージ 'Microsoft.Extensions.Caching.Memory' 8.0.0 に既知の 高 重大度の脆弱性があります
warning NU1903: パッケージ 'System.Text.Json' 8.0.4 に既知の 高 重大度の脆弱性があります
warning NU1903: パッケージ 'Microsoft.Extensions.Caching.Memory' 8.0.0 に既知の 高 重大度の脆弱性があります
warning NU1903: パッケージ 'System.Text.Json' 8.0.4 に既知の 高 重大度の脆弱性があります