diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs
index 1f94777..9fb3ca6 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs
@@ -2,6 +2,7 @@
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
+using Pf2eModel;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
@@ -11,11 +12,6 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
///
internal class AonprdDataBuilder(AonprdLoader loader)
{
- ///
- /// Класс работы с базой данных.
- ///
- public readonly Pf2eDatabase Database = Pf2eDatabase.Instance;
-
///
/// Основной класс для работы с данными Архива Нефиса.
///
@@ -38,7 +34,10 @@ internal class AonprdDataBuilder(AonprdLoader loader)
{
Console.WriteLine("Truncating all old data...");
- Database.ExecuteCommand("SELECT f_truncate_tables();");
+ using Pf2eDbContext context = new();
+ string cmd = context.Entities.Truncate() + "\n" + context.TruncateAll();
+
+ Console.WriteLine(cmd);
Console.WriteLine("Truncation completed!");
}
diff --git a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/PF2eFoundryDataBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/PF2eFoundryDataBuilder.cs
index 0249020..cd88174 100644
--- a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/PF2eFoundryDataBuilder.cs
+++ b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/PF2eFoundryDataBuilder.cs
@@ -2,13 +2,12 @@
using Konsole;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
+using Pf2eModel;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.DataBuilder;
internal class PF2eFoundryDataBuilder
{
- public Pf2eDatabase Database = Pf2eDatabase.Instance;
-
///
/// Загрузчик данных.
///
@@ -51,7 +50,10 @@ internal class PF2eFoundryDataBuilder
{
Console.WriteLine("Truncating all old data...");
- Database.ExecuteCommand("SELECT f_truncate_tables();");
+ using Pf2eDbContext context = new();
+ string cmd = context.Entities.Truncate() + "\n" + context.TruncateAll();
+
+ Console.WriteLine(cmd);
Console.WriteLine("Truncation completed!");
}
diff --git a/Pf2eDataScrapper/Pf2eDatabase.cs b/Pf2eDataScrapper/Pf2eDatabase.cs
deleted file mode 100644
index b3197f6..0000000
--- a/Pf2eDataScrapper/Pf2eDatabase.cs
+++ /dev/null
@@ -1,152 +0,0 @@
-using System.Text.RegularExpressions;
-using Npgsql;
-
-namespace Pf2eDataScrapper;
-
-///
-/// Main class for database connection and operations.
-///
-public partial class Pf2eDatabase
-{
- ///
- /// Подключение к базе данных.
- ///
- public readonly NpgsqlConnection Connection;
-
- ///
- /// Имя пользователя для подключения к базе данных.
- ///
- public readonly string Username;
-
- ///
- /// Название базы данных.
- ///
- public readonly string DatabaseName;
-
- private Pf2eDatabase()
- {
- Username = UsernamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
- DatabaseName = DatabaseNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
-
- Connection = new(EnvSettings.DatabaseConnection);
- Connection.Open();
- }
-
- ~Pf2eDatabase()
- {
- Connection.Close();
- }
-
- ///
- /// Singleton instance of the database connection manager.
- ///
- public static Pf2eDatabase Instance { get; } = new();
-
- ///
- /// Current transaction associated with this database connection.
- ///
- public NpgsqlTransaction? Transaction { get; private set; }
-
- ///
- /// Begin a new transaction on the database connection.
- ///
- public void StartTransaction()
- {
- if (Transaction != null)
- {
- throw new InvalidOperationException("Transaction already started.");
- }
-
- Transaction = Connection.BeginTransaction();
- }
-
- ///
- /// Commit the current transaction to the database.
- ///
- public void Commit()
- {
- if (Transaction == null)
- {
- throw new InvalidOperationException("No transaction started.");
- }
-
- Transaction.Commit();
- }
-
- ///
- /// Execute a SQL command against the database.
- ///
- /// The SQL command to execute
- public void ExecuteCommand(string sql)
- {
- NpgsqlCommand command = GetCommand(sql);
-
- command.ExecuteNonQuery();
- }
-
- ///
- /// Creates a new SQL command for the database connection.
- ///
- /// The SQL command string
- /// A new NpgsqlCommand object
- public NpgsqlCommand GetCommand(string sql)
- {
- return new(sql, Connection);
- }
-
- /*
- public void RebuildDatabase()
- {
- // Закрываем текущее соединение
- Connection.Close();
-
- // Получение имён
- var databaseNamePattern = DatabaseNamePattern();
- var userConnection = databaseNamePattern.Replace(EnvSettings.DatabaseConnection, ";");
- var databaseName = databaseNamePattern.Match(EnvSettings.DatabaseConnection).Groups[1].Value;
- var username = UserNamePattern().Match(EnvSettings.DatabaseConnection).Groups[1].Value;
-
- // Временное подключение к базе данных
- NpgsqlConnection tmpConnection = new(userConnection);
- tmpConnection.Open();
-
- // Удаляем базу данных
- NpgsqlCommand dropCommand = new($"DROP DATABASE \"{databaseName}\";", tmpConnection);
- dropCommand.ExecuteNonQuery();
-
- // Создаём новую базу данных
- NpgsqlCommand createCommand = new($@"
- CREATE DATABASE {databaseName}
- WITH OWNER = {username}
- ENCODING = 'UTF8'
- CONNECTION LIMIT = -1;
- ", tmpConnection);
- createCommand.ExecuteNonQuery();
-
- string[] permissionCommands = [
- $"GRANT TEMPORARY, CONNECT ON DATABASE {databaseName} TO PUBLIC;",
- $"GRANT ALL ON DATABASE {databaseName} TO {username} WITH GRANT OPTION;",
- $"GRANT ALL ON DATABASE {databaseName} TO postgres;"
- ];
-
- foreach (var command in permissionCommands)
- {
- NpgsqlCommand permissionCommand = new(command, tmpConnection);
- permissionCommand.ExecuteNonQuery();
- }
-
- // Закрываем временное соединение
- tmpConnection.Close();
-
- // Заново создаём соединение
- Connection = new NpgsqlConnection(EnvSettings.DatabaseConnection);
- }
-
- */
-
- [GeneratedRegex(@"(?:^|;)Database=(.*?)(?:$|;)")]
- private static partial Regex DatabaseNamePattern();
-
- [GeneratedRegex(@"(?:^|;)User=(.*?)(?:$|;)")]
- private static partial Regex UsernamePattern();
-}
diff --git a/Pf2eModel/EfCleanHelper.cs b/Pf2eModel/EfCleanHelper.cs
new file mode 100644
index 0000000..1cb9cee
--- /dev/null
+++ b/Pf2eModel/EfCleanHelper.cs
@@ -0,0 +1,97 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+
+namespace Pf2eModel;
+
+///
+/// Класс, обеспецивающий общий способ для отчистки данных таблиц.
+///
+/// Источник: https://www.codeproject.com/Articles/5339402/Delete-All-Rows-in-Entity-Framework-Core-6
+///
+public static class EfCleanHelper
+{
+ ///
+ /// Определение полного названия таблицы по служебному классу.
+ ///
+ /// Служебный класс, определяющий таблицу базы данных.
+ /// Схема таблицы по умолчанию ("public" для PostgresSQL)
+ ///
+ private static string GetName(IEntityType entityType, string defaultSchemaName = "public")
+ {
+ object? schema = entityType.FindAnnotation("Relational:Schema")?.Value;
+
+ string tableName =
+ entityType.GetAnnotation("Relational:TableName")?.Value?.ToString()
+ ?? throw new($"Не могу определить название таблицы для типа {entityType}");
+
+ string schemaName = schema?.ToString() ?? defaultSchemaName;
+
+ string name = $"\"{schemaName}\".\"{tableName}\"";
+ return name;
+ }
+
+ ///
+ /// Определение полного названия таблицы по типу сущности.
+ ///
+ /// Тип искомой сущности.
+ /// Контекст, в котором производится поиск таблицы.
+ /// Строковое представление названия таблицы.
+ public static string TableName(DbContext dbContext)
+ where T : class
+ {
+ IEntityType entityType =
+ dbContext.Model.FindEntityType(typeof(T))
+ ?? throw new($"Не могу определить название таблицы для класса {typeof(T)}");
+
+ return GetName(entityType);
+ }
+
+ ///
+ /// Определение полного названия таблицы по типу сущности.
+ ///
+ /// Тип искомой сущности.
+ /// CLR-представление таблицы, название которой требуется определить.
+ /// Строковое представление названия таблицы.
+ public static string TableName(DbSet dbSet)
+ where T : class
+ {
+ IEntityType entityType = dbSet.EntityType;
+ return GetName(entityType);
+ }
+
+ ///
+ /// Отчистка содержимого таблицы.
+ ///
+ /// Тип сущности, таблицу которой необходимо отчистить.
+ /// CLR-представление таблицы, которую требуется отчистить.
+ /// Строковое представление выполненной SQL-команды.
+ public static string Truncate(this DbSet dbSet)
+ where T : class
+ {
+ string cmd = $"TRUNCATE TABLE {TableName(dbSet)} CASCADE;";
+ DbContext context = dbSet.GetService().Context;
+ context.Database.ExecuteSqlRaw(cmd);
+ return cmd;
+ }
+
+ ///
+ /// Получение SQL-команды отчистки содержимого всех таблиц базы данных.
+ ///
+ /// Контекст базы данных, в которой необходимо отчистить все строки.
+ /// Строковое представление выполненных SQL-команды.
+ public static string TruncateAll(this DbContext context)
+ {
+ IEnumerable tables = context.Model.GetEntityTypes();
+ List cmdList = [];
+
+ foreach (IEntityType entityType in tables)
+ {
+ string cmd = $"TRUNCATE TABLE {GetName(entityType)} CASCADE;";
+ context.Database.ExecuteSqlRaw(cmd);
+ cmdList.Add(cmd);
+ }
+
+ return string.Join('\n', cmdList);
+ }
+}
diff --git a/Pf2eModel/Migrations/20250418062358_Function.cs b/Pf2eModel/Migrations/20250418062358_Function.cs
index d735d1d..f26cf8e 100644
--- a/Pf2eModel/Migrations/20250418062358_Function.cs
+++ b/Pf2eModel/Migrations/20250418062358_Function.cs
@@ -12,25 +12,6 @@ public partial class Function : Migration
{
migrationBuilder.Sql(
"""
-CREATE OR REPLACE FUNCTION f_truncate_tables()
- RETURNS void
- LANGUAGE plpgsql AS
-$func$
-BEGIN
- EXECUTE
- (SELECT 'TRUNCATE TABLE '
- || string_agg(format('%I.%I', schemaname, tablename), ', ')
- || ' CASCADE;'
- FROM pg_tables
- WHERE schemaname = 'public'
- AND tablename != '__EFMigrationsHistory'
- );
-END
-$func$;
-"""
- );
- migrationBuilder.Sql(
- """
CREATE OR REPLACE FUNCTION f_drop_tables()
RETURNS void
LANGUAGE plpgsql AS
diff --git a/Pf2eModel/Pf2eDbContextModel.cs b/Pf2eModel/Pf2eDbContextModel.cs
index 5966a03..99e5fb6 100644
--- a/Pf2eModel/Pf2eDbContextModel.cs
+++ b/Pf2eModel/Pf2eDbContextModel.cs
@@ -52,7 +52,12 @@ public partial class Pf2eDbContext
private void BindEntity(EntityTypeBuilder entity)
where T : class, IEntity
{
- entity.HasOne(e => e.Entity).WithOne().HasForeignKey(e => e.Id).IsRequired();
+ entity
+ .HasOne(e => e.Entity)
+ .WithOne()
+ .HasForeignKey(e => e.Id)
+ .IsRequired()
+ .OnDelete(DeleteBehavior.Cascade);
}
///