ハロの外部記憶インターフェイス

そろそろ覚える努力が必要かも…

OracleのDbContextを作成し、Web APIからコードファスト作成を行う

BLL

  • Nuget   - Oracle.EntityFrameworkCore   - Oracle.ManagedDataAccess.Core

    DbModelBase

    ConcurrencyCheck:ータが更新される際に、データベースのバージョンがメモリ上のバージョンと一致しているかを確認し、不一致の場合はDbUpdateConcurrencyExceptionをスローする。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public interface IDbModelBase
{
    int Idno { get; set; }
    DateTime CreatedAt { get; set; }
    DateTime UpdatedAt { get; set; }
    int VersionNo { get; set; }
    string? IsActive { get; set; }
}

public class DbModelBase : IDbModelBase
{
    [Column("IDNO"), Key]
    public int Idno { get; set; }
    [Column("CREATED_AT")]
    public DateTime CreatedAt { get; set; }
    [Column("UPDATED_AT")]
    public DateTime UpdatedAt { get; set; }
    [Column("VERSION_NO"), ConcurrencyCheck]
    public int VersionNo { get; set; }
    [Column("IS_ACTIVE")]
    public string? IsActive { get; set; }
}

DbContext

OnBeforeSavingで共通項目の自動設定を行える。

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
    public override int SaveChanges()
    {
        OnBeforeSaving();
        return base.SaveChanges();
    }
    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        OnBeforeSaving();
        return base.SaveChangesAsync(cancellationToken);
    }
    private void OnBeforeSaving()
    {
        var entries = ChangeTracker.Entries<IDbModelBase>();
        foreach (var entry in entries)
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.CreatedAt = DateTime.UtcNow;
                entry.Entity.UpdatedAt = DateTime.UtcNow;
            }
            else if (entry.State == EntityState.Modified)
            {
                // 更新時、CreatedAtは変更しない
                entry.Property(p => p.CreatedAt).IsModified = false;
                entry.Entity.UpdatedAt = DateTime.UtcNow;
            }
        }
    }
}

try-catchで処理する

try
{
    var product = _context.Products.Find(1); // データを取得
    if (product == null)
    {
        // 処理なし
    }
    product.Price = 12.34m; // データを変更
    _context.SaveChanges(); // 楽観的ロックチェック
}
catch (DbUpdateConcurrencyException ex)
{
    // 競合が発生した場合の処理
    // 例: ユーザーにエラーメッセージを表示し、再ロードを促す
    // データベース上の最新の値を画面に表示するなど
}

IDENTITY

modelBuilder.HasSequence<int>("USER_SEQ", schema: "YOUR_SCHEMA")
    .StartsAt(1)
    .IncrementsBy(1);

modelBuilder.Entity<User>()
    .Property(u => u.Id)
    .HasDefaultValueSql("USER_SEQ.NEXTVAL");

Index宣言

[Index(nameof(Email), IsUnique = true, Name = "IDX_USER_EMAIL")]
public class User
{
    public int Id { get; set; }
    public string Email { get; set; }
}

Web API

コードファストのマイグレーションコマンドは WebAPI プロジェクトを起点に実行する必要があり * Nuget   - Oracle.EntityFrameworkCore   - Oracle.ManagedDataAccess.Core   - Microsoft.EntityFrameworkCore.Design   - Swashbuckle.AspNetCore - 参照 - BLL

Appsetting.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "OracleDbContext": "User Id=ORCL;Password=ORCL;Data Source=localhost/ORCL;"
  }
}

Program.cs

builder.Services.AddDbContext<AppDbContext>(options =>    options.UseOracle(builder.Configuration.GetConnectionString("OracleDbContext")));

Controller

DIされたAppDbContextをコントローラの初期化時に取得する。

[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    private readonly AppDbContext _context;
    public MyController(AppDbContext context)
    {
        _context = context;
    }
}

マイグレーション

プロジェクトにEF Coreのツールを追加

dotnet add package Microsoft.EntityFrameworkCore.Design

Visual Studio → ツール → NuGet パッケージマネージャー → パッケージマネージャー コンソール

Add-Migration InitialCreate
Update-Database
Remove-Migration

.net cliの場合

dotnet tool install --global dotnet-ef

dotnet ef migrations add InitialCreate \
  --project pjname \
  --startup-project pjname

dotnet ef migrations add InitialCreate
dotnet ef database update
dotnet ef migrations remove

出力する場合

dotnet ef migrations add InitialCreate \
  --project rf_bll \
  --startup-project rf_api \
  --output-dir Migrations

クライントにOpen APIサービス追加

追加 サービス参照 OpenAPI

クライアントを生成

var sc = new rf_api_client("http://localhost:5086", new HttpClient());
ICollection<WeatherForecast> forecasts = await sc.GetWeatherForecastAsync();
foreach (var forecast in forecasts)
{
    MessageBox.Show($"Date: {forecast.Date}, TempC: {forecast.TemperatureC}, Summary: {forecast.Summary}");
}

SQL 実行

sqlplus myuser/mypassword@localhost/ORCL @"C:\scripts\init_schema.sql"