feat: work on setting up database

This commit is contained in:
Stevan Freeborn
2024-08-18 20:16:38 -05:00
parent f9e6e54db6
commit 3c809de5dc
19 changed files with 745 additions and 1 deletions
@@ -0,0 +1,197 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SanctionsSearch.Worker.Persistence;
#nullable disable
namespace SanctionsSearch.Worker.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20240819001854_initial_migration")]
partial class initial_migration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
modelBuilder.Entity("SanctionsSearch.Worker.Models.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CityProvincePostal")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("SdnId")
.HasColumnType("INTEGER");
b.Property<string>("StreetAddress")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("SdnId");
b.ToTable("Addresses");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Alias", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("SdnId")
.HasColumnType("INTEGER");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("SdnId");
b.ToTable("Aliases");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Comment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("SdnId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SdnId");
b.ToTable("Comments");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Sdn", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CallSign")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("GrossRegisteredTonnage")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Program")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Tonnage")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("VesselFlag")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("VesselOwner")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("VesselType")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Sdns");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Address", b =>
{
b.HasOne("SanctionsSearch.Worker.Models.Sdn", "Sdn")
.WithMany()
.HasForeignKey("SdnId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Sdn");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Alias", b =>
{
b.HasOne("SanctionsSearch.Worker.Models.Sdn", "Sdn")
.WithMany("Aliases")
.HasForeignKey("SdnId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Sdn");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Comment", b =>
{
b.HasOne("SanctionsSearch.Worker.Models.Sdn", "Sdn")
.WithMany()
.HasForeignKey("SdnId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Sdn");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Sdn", b =>
{
b.Navigation("Aliases");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,133 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace SanctionsSearch.Worker.Migrations
{
/// <inheritdoc />
public partial class initial_migration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Sdns",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", nullable: false),
Type = table.Column<string>(type: "TEXT", nullable: false),
Program = table.Column<string>(type: "TEXT", nullable: false),
Title = table.Column<string>(type: "TEXT", nullable: false),
CallSign = table.Column<string>(type: "TEXT", nullable: false),
VesselType = table.Column<string>(type: "TEXT", nullable: false),
Tonnage = table.Column<string>(type: "TEXT", nullable: false),
GrossRegisteredTonnage = table.Column<string>(type: "TEXT", nullable: false),
VesselFlag = table.Column<string>(type: "TEXT", nullable: false),
VesselOwner = table.Column<string>(type: "TEXT", nullable: false),
Remarks = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Sdns", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Addresses",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
StreetAddress = table.Column<string>(type: "TEXT", nullable: false),
CityProvincePostal = table.Column<string>(type: "TEXT", nullable: false),
Country = table.Column<string>(type: "TEXT", nullable: false),
Remarks = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Addresses", x => x.Id);
table.ForeignKey(
name: "FK_Addresses_Sdns_SdnId",
column: x => x.SdnId,
principalTable: "Sdns",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Aliases",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
Type = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Remarks = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Aliases", x => x.Id);
table.ForeignKey(
name: "FK_Aliases_Sdns_SdnId",
column: x => x.SdnId,
principalTable: "Sdns",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Comments",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
Remarks = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Comments", x => x.Id);
table.ForeignKey(
name: "FK_Comments_Sdns_SdnId",
column: x => x.SdnId,
principalTable: "Sdns",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Addresses_SdnId",
table: "Addresses",
column: "SdnId");
migrationBuilder.CreateIndex(
name: "IX_Aliases_SdnId",
table: "Aliases",
column: "SdnId");
migrationBuilder.CreateIndex(
name: "IX_Comments_SdnId",
table: "Comments",
column: "SdnId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Addresses");
migrationBuilder.DropTable(
name: "Aliases");
migrationBuilder.DropTable(
name: "Comments");
migrationBuilder.DropTable(
name: "Sdns");
}
}
}
@@ -0,0 +1,194 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SanctionsSearch.Worker.Persistence;
#nullable disable
namespace SanctionsSearch.Worker.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
modelBuilder.Entity("SanctionsSearch.Worker.Models.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CityProvincePostal")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("SdnId")
.HasColumnType("INTEGER");
b.Property<string>("StreetAddress")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("SdnId");
b.ToTable("Addresses");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Alias", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("SdnId")
.HasColumnType("INTEGER");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("SdnId");
b.ToTable("Aliases");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Comment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("SdnId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SdnId");
b.ToTable("Comments");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Sdn", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CallSign")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("GrossRegisteredTonnage")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Program")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Remarks")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Tonnage")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("VesselFlag")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("VesselOwner")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("VesselType")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Sdns");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Address", b =>
{
b.HasOne("SanctionsSearch.Worker.Models.Sdn", "Sdn")
.WithMany()
.HasForeignKey("SdnId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Sdn");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Alias", b =>
{
b.HasOne("SanctionsSearch.Worker.Models.Sdn", "Sdn")
.WithMany("Aliases")
.HasForeignKey("SdnId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Sdn");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Comment", b =>
{
b.HasOne("SanctionsSearch.Worker.Models.Sdn", "Sdn")
.WithMany()
.HasForeignKey("SdnId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Sdn");
});
modelBuilder.Entity("SanctionsSearch.Worker.Models.Sdn", b =>
{
b.Navigation("Aliases");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,13 @@
namespace SanctionsSearch.Worker.Models;
class Address
{
public int SdnId { get; set; }
public int Id { get; set; }
public string StreetAddress { get; set; } = string.Empty;
public string CityProvincePostal { get; set; } = string.Empty;
public string Country { get; set; } = string.Empty;
public string Remarks { get; set; } = string.Empty;
public virtual Sdn Sdn { get; set; } = default!;
}
@@ -0,0 +1,12 @@
namespace SanctionsSearch.Worker.Models;
class Alias
{
public int SdnId { get; set; }
public int Id { get; set; }
public string Type { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Remarks { get; set; } = string.Empty;
public virtual Sdn Sdn { get; set; } = default!;
}
@@ -0,0 +1,10 @@
namespace SanctionsSearch.Worker.Models;
class Comment
{
public int SdnId { get; set; }
public int Id { get; set; }
public string Remarks { get; set; } = string.Empty;
public virtual Sdn Sdn { get; set; } = default!;
}
+19
View File
@@ -0,0 +1,19 @@
namespace SanctionsSearch.Worker.Models;
class Sdn
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public string Program { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string CallSign { get; set; } = string.Empty;
public string VesselType { get; set; } = string.Empty;
public string Tonnage { get; set; } = string.Empty;
public string GrossRegisteredTonnage { get; set; } = string.Empty;
public string VesselFlag { get; set; } = string.Empty;
public string VesselOwner { get; set; } = string.Empty;
public string Remarks { get; set; } = string.Empty;
public virtual ICollection<Alias> Aliases { get; set; } = [];
}
@@ -0,0 +1,6 @@
namespace SanctionsSearch.Worker.Options;
class DbOptions
{
public string DatabaseName { get; set; } = string.Empty;
}
@@ -0,0 +1,40 @@
using System.Reflection;
using Microsoft.Data.Sqlite;
namespace SanctionsSearch.Worker.Persistence;
class AppDbContext(IOptionsSnapshot<DbOptions> options) : DbContext, IDisposable
{
private readonly IOptionsSnapshot<DbOptions> _options = options;
private SqliteConnection? _connection;
public DbSet<Sdn> Sdns { get; set; } = default!;
public DbSet<Address> Addresses { get; set; } = default!;
public DbSet<Alias> Aliases { get; set; } = default!;
public DbSet<Comment> Comments { get; set; } = default!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
var dataDir = Path.Combine(path, "SanctionsSearch", "Data");
if (Directory.Exists(dataDir) == false)
{
Directory.CreateDirectory(dataDir);
}
var dbPath = Path.Combine(dataDir, _options.Value.DatabaseName);
_connection = new SqliteConnection($"Data Source={dbPath}");
_connection.Open();
optionsBuilder.UseSqlite(_connection);
}
public override void Dispose()
{
_connection?.Dispose();
base.Dispose();
}
}
+25
View File
@@ -1,3 +1,12 @@
using SanctionsSearch.Worker.Persistence;
using SanctionsSearch.Worker.Setup;
if (EF.IsDesignTime)
{
Host.CreateDefaultBuilder().Build().Run();
return;
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("Application", "SanctionsSearch.Worker")
.Enrich.WithEnvironmentName()
@@ -19,9 +28,25 @@ try
builder.Logging.ClearProviders();
builder.Logging.AddSerilog();
builder.Services.ConfigureOptions<OfacFileServiceOptionsSetup>();
builder.Services.ConfigureOptions<DbOptionsSetup>();
builder.Services.AddDbContext<AppDbContext>();
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
using var scope = host.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();
if (pendingMigrations.Any())
{
Log.Information("Applying pending migrations");
await context.Database.MigrateAsync();
}
host.Run();
Log.Information("Stopping Sanctions Search worker");
@@ -10,6 +10,11 @@
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="FluentResults" Version="3.16.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
Binary file not shown.
@@ -0,0 +1,9 @@
namespace SanctionsSearch.Worker.Setup;
class DbOptionsSetup(IConfiguration configuration) : IConfigureOptions<DbOptions>
{
private const string SectionName = nameof(DbOptions);
private readonly IConfiguration _configuration = configuration;
public void Configure(DbOptions options) => _configuration.GetSection(SectionName).Bind(options);
}
@@ -0,0 +1,9 @@
namespace SanctionsSearch.Worker.Setup;
class OfacFileServiceOptionsSetup(IConfiguration configuration) : IConfigureOptions<OfacFileServiceOptions>
{
private const string SectionName = nameof(OfacFileServiceOptions);
private readonly IConfiguration _configuration = configuration;
public void Configure(OfacFileServiceOptions options) => _configuration.GetSection(SectionName).Bind(options);
}
+3
View File
@@ -3,7 +3,10 @@ global using Microsoft.Extensions.Options;
global using SanctionsSearch.Worker.Interfaces;
global using SanctionsSearch.Worker.Options;
global using SanctionsSearch.Worker;
global using SanctionsSearch.Worker.Models;
global using Serilog;
global using Serilog.Exceptions;
global using Serilog.Formatting.Compact;
global using Microsoft.EntityFrameworkCore;
@@ -15,5 +15,8 @@
"ConAddressesFileName": "CONS_ADD.CSV",
"ConAltNamesFileName": "CONS_ALT.CSV",
"ConCommentsFileName": "CONS_COMMENTS.CSV"
},
"DbOptions": {
"DatabaseName": "SanctionsSearch.example.db"
}
}