【個人的メモ】C# .netCore3.1 と EntityFrameworkCore3.1 と SQLite3 と NLog の連携基礎
.netCore3.1 と EntityFrameworkCore3.1.5 と SQLite3 と NLog4.7.2 で連携する場合の基礎クラス
Myルール:
1.基本的には短いスコープで使用する
2.必ずusing を使用すること
3.処理の更新データが不要 かつ 処理が思う長い場合、継承して作ること
4.他処理のデータ更新を反映する必要がある場合は、短いスコープ範囲で処理すること
(Context を new したタイミング(正確には違うが…)で未連携状態になるため)
機能:
1.DB接続
2.SQLログ出力
3.追加日時の自動追加
4.更新日時の自動追加
5.DBの自動作成
6.DBの再作成
7.Trancate(SQLiteなのでDelete) メソッド
8.統計情報更新機能
サンプル機能:
1.DBSetの書き方
2.PKの指定方法
3.Indexの作成方法
4.固定データの初期登録機能
SampleContext クラス
LoggerProvider クラス
LoggerSQL クラス
検索用:C# .net Core 3.1 Entity Framework Core 3.1 SQLite3 NLog 連携 ログ出力 やり方 作り方 共通化
Myルール:
1.基本的には短いスコープで使用する
2.必ずusing を使用すること
3.処理の更新データが不要 かつ 処理が思う長い場合、継承して作ること
4.他処理のデータ更新を反映する必要がある場合は、短いスコープ範囲で処理すること
(Context を new したタイミング(正確には違うが…)で未連携状態になるため)
機能:
1.DB接続
2.SQLログ出力
3.追加日時の自動追加
4.更新日時の自動追加
5.DBの自動作成
6.DBの再作成
7.Trancate(SQLiteなのでDelete) メソッド
8.統計情報更新機能
サンプル機能:
1.DBSetの書き方
2.PKの指定方法
3.Indexの作成方法
4.固定データの初期登録機能
SampleContext クラス
namespace Sample.Models
{
/// <summary>
/// DBアクセスコンテキスト
/// </summary>
class SampleContext : DbContext
{
/// <summary>
/// ログクラス
/// </summary>
protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
/// <summary>
/// DBファイルの保存先パス
/// </summary>
const string DataBase_FilePath = @"./Sample.db";
/// <summary>
/// Sampleテーブル
/// </summary>
internal DbSet<SampleEntity> Samples { get; set; }
#region DB接続情報
/// <summary>
/// 接続先情報を指定します。
/// </summary>
/// <param name="optionsBuilder">オプション設定クラス</param>
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// DBログ
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddProvider(new LoggerProvider())
.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name &&
level == LogLevel.Information);
});
var connectionString = new SqliteConnectionStringBuilder { DataSource = DataBase_FilePath }.ToString();
optionsBuilder.UseSqlite(new SqliteConnection(connectionString))
.EnableSensitiveDataLogging()
.UseLoggerFactory(loggerFactory);
}
#endregion DB接続情報
/// <summary>
/// DB反映時の事前処理
/// </summary>
/// <param name="acceptAllChangesOnSuccess"></param>
/// <returns>更新数</returns>
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
// 追加日時、更新日時を設定
SetAddAndUpdateDateColumn();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
/// <summary>
/// DB反映時の事前処理
/// </summary>
/// <param name="acceptAllChangesOnSuccess"></param>
/// <param name="cancellationToken"></param>
/// <returns>タスク</returns>
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
// 追加日時、更新日時を設定
SetAddAndUpdateDateColumn();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
/// <summary>
/// 追加日時、更新日時を設定します
/// </summary>
void SetAddAndUpdateDateColumn()
{
try
{
// 追加データの一覧取得
var AddedEntities = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added)
.ToList();
var nowDate = DateTime.Now;
// 簡単な処理なのでスレッド数制限しない
Parallel.ForEach(AddedEntities, e =>
{
// 追加日時を設定
if (e.Property("AddDate").CurrentValue == null)
{
e.Property("AddDate").CurrentValue = nowDate;
}
// 更新日時を設定
if (e.Property("UpdateDate").CurrentValue == null)
{
e.Property("UpdateDate").CurrentValue = nowDate;
}
});
// 更新データの一覧を取得
var EditedEntities = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Modified)
.ToList();
// 簡単な処理なのでスレッド数制限しない
Parallel.ForEach(EditedEntities, e =>
{
// 追加日時を設定
if (e.Property("AddDate").CurrentValue == null)
{
e.Property("AddDate").CurrentValue = nowDate;
}
// 更新日時を設定
e.Property("UpdateDate").CurrentValue = nowDate;
});
}
catch (Exception ex)
{
Logger.Error(ex, "追加日時、更新日時の設定に失敗しました。");
}
}
/// <summary>
/// データベースファイルを初期化します。
/// 既に初期化済みの場合は何も行いません。
/// </summary>
internal async void InitializeDataBase()
{
// テーブルを作成
Logger.Info("データベースファイルを新規作成します。");
await Database.EnsureCreatedAsync();
// 初期データ投入
InsertMasterData();
}
/// <summary>
/// DBファイルを作り直します。
/// </summary>
internal void ReInitializeDataBase()
{
if (File.Exists(DataBase_FilePath))
{
Logger.Warn("データベースファイルを削除します。");
Database.CloseConnection();
File.Delete(DataBase_FilePath);
}
// 初期化処理を実行
InitializeDataBase();
}
/// <summary>
/// DBに作成するテーブル情報を設定します。
/// </summary>
/// <param name="modelBuilder">モデル作成クラス</param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// テーブル作成処理
SampleEntityInfo(modelBuilder);
}
/// <summary>
/// サンプルテーブルの情報を指定します
/// </summary>
/// <param name="modelBuilder">モデル作成クラス</param>
private void SampleEntityInfo(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SampleEntity>(
e =>
{
// PKの指定
e.HasKey(c => new { c.Id }).HasName("PK_Sample_Id");
// Indexの作成
e.HasIndex(c => new { c.SampleA, c.SampleB });
});
}
/// <summary>
/// 初期データを投入します。
/// </summary>
private async void InsertMasterData()
{
try
{
// トランザクション開始
await Database.BeginTransactionAsync();
// 初期データを作成
InsertMasterSampleData();
await SaveChangesAsync();
Database.CommitTransaction();
}
catch (Exception ex)
{
Logger.Error(ex, "初期データ投入に失敗しました。");
Database.RollbackTransaction();
}
}
/// <summary>
/// サンプルデータの初期データを作成します。
/// </summary>
private void InsertMasterSampleData()
{
var tmp = new SampleEntity();
if (!Samples.Where(x => x.Id == tmp.Id).Any())
{
Samples.Add(new SampleEntity
{
Id = 1,
SampleA = "",
SampleB = ""
});
}
}
/// <summary>
/// <para>テーブルの全で他を削除します。</para>
/// <para>SQLite は Truncateが無いため、Delete分を流します</para>
/// <para>EntityにTable Attribute が指定されている前提です</para>
/// <para>SQL直なのでDBSetと整合性が取れなくなるので、処理前(new直後等)のみの仕様に制限してください。</para>
/// </summary>
/// <param name="type">Entityのタイプ</param>
public void Truncate(Type type)
{
bool isDeleted = false;
Attribute[] attrs = Attribute.GetCustomAttributes(type);
foreach (Attribute attr in attrs)
{
if (attr is TableAttribute table)
{
Database.ExecuteSqlRaw($"DELETE FROM {table.Name};");
isDeleted = true;
break;
}
}
if (!isDeleted)
{
// テーブル属性が無い場合はクラス名で削除しに行く
Database.ExecuteSqlRaw($"DELETE FROM {type.Name};");
}
}
/// <summary>
/// <para>テーブル情報の統計情報を更新します</para>
/// </summary>
/// <param name="type">Entityのタイプ</param>
public void Analyze(Type type)
{
bool isDeleted = false;
Attribute[] attrs = Attribute.GetCustomAttributes(type);
foreach (Attribute attr in attrs)
{
if (attr is TableAttribute table)
{
Database.ExecuteSqlRaw($"Analyze {table.Name};");
isDeleted = true;
break;
}
}
if (!isDeleted)
{
// テーブル属性が無い場合はクラス名で解析する
Database.ExecuteSqlRaw($"Analyze {type.Name};");
}
}
}
}
LoggerProvider クラス
using Microsoft.Extensions.Logging;
namespace ChannelAssist.Models.Logger
{
/// <summary>
/// ログ出力プロバイダ
/// </summary>
class LoggerProvider : ILoggerProvider
{
/// <summary>
/// ログクラス作成
/// </summary>
/// <param name="className"></param>
/// <returns>Microsoft用ログクラス</returns>
public ILogger CreateLogger(string className)
{
return new LoggerSQL();
}
/// <summary>
/// オブジェクトの破棄
/// </summary>
public void Dispose() {
}
}
}
LoggerSQL クラス
using Microsoft.Extensions.Logging;
using System;
namespace ChannelAssist.Models.Logger
{
/// <summary>
/// SQLログを出力するクラス
/// </summary>
class LoggerSQL : ILogger
{
/// <summary>
/// NLogクラス
/// </summary>
private readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
/// <summary>
///
/// </summary>
/// <typeparam name="TState"></typeparam>
/// <param name="state"></param>
/// <returns></returns>
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
/// <summary>
/// ログ出力可否
/// </summary>
/// <param name="logLevel"></param>
/// <returns>true:ログ出力可能、false:ログ出力不可</returns>
public bool IsEnabled(LogLevel logLevel)
{
bool result = false;
if (Logger.IsDebugEnabled)
{
// SQLログはデバッグの時のみ出力したい
result = true;
}
return result;
}
/// <summary>
/// ログ出力
/// </summary>
/// <typeparam name="TState"></typeparam>
/// <param name="logLevel"></param>
/// <param name="eventId"></param>
/// <param name="state"></param>
/// <param name="exception"></param>
/// <param name="formatter"></param>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
// SQLはDebugとして出力する
Logger.Debug(exception, formatter(state, exception));
}
}
}
検索用:C# .net Core 3.1 Entity Framework Core 3.1 SQLite3 NLog 連携 ログ出力 やり方 作り方 共通化
コメント
コメントを投稿