.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 連携 ログ出力 やり方 作り方 共通化