feat: added Sdn repo and added some initial tests
This commit is contained in:
Vendored
+2
-1
@@ -4,6 +4,7 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"OFAC",
|
"OFAC",
|
||||||
"Sdns",
|
"Sdns",
|
||||||
"Szalay"
|
"Szalay",
|
||||||
|
"upserting"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using Bogus;
|
|
||||||
|
|
||||||
namespace SanctionsSearch.Worker.Tests.Fakes;
|
namespace SanctionsSearch.Worker.Tests.Fakes;
|
||||||
|
|
||||||
class SdnFaker : Faker<Sdn>
|
class SdnFaker : Faker<Sdn>
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
using SanctionsSearch.Worker.Tests.Fakes;
|
|
||||||
|
|
||||||
namespace SanctionsSearch.Worker.Tests.Integration;
|
|
||||||
|
|
||||||
public class EfRepositoryTests : IAsyncLifetime
|
|
||||||
{
|
|
||||||
private readonly AppDbContext _context;
|
|
||||||
private readonly EfRepository<Sdn> _sdnRepository;
|
|
||||||
private readonly SdnFaker _sdnFaker = new();
|
|
||||||
|
|
||||||
public EfRepositoryTests()
|
|
||||||
{
|
|
||||||
var loggerFactory = LoggerFactory.Create(builder => builder.ClearProviders());
|
|
||||||
|
|
||||||
_context = new AppDbContext(new DbOptions { DatabaseName = $"{Guid.NewGuid()}.db" });
|
|
||||||
_sdnRepository = new EfRepository<Sdn>(_context, loggerFactory.CreateLogger<EfRepository<Sdn>>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Upsert_WithNewEntity_ShouldAddEntityToDatabase()
|
|
||||||
{
|
|
||||||
var sdn = _sdnFaker.Generate();
|
|
||||||
|
|
||||||
await _sdnRepository.Upsert(sdn);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
var result = await _context.Sdns.FindAsync(sdn.Id);
|
|
||||||
|
|
||||||
result.Should().BeEquivalentTo(sdn);
|
|
||||||
result!.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
|
||||||
result.UpdatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Upsert_WithExistingEntity_ShouldUpdateEntityInDatabase()
|
|
||||||
{
|
|
||||||
var sdn = _sdnFaker.Generate();
|
|
||||||
|
|
||||||
await _sdnRepository.Upsert(sdn);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
var createdSdn = await _context.Sdns.FindAsync(sdn.Id);
|
|
||||||
|
|
||||||
createdSdn.Should().BeEquivalentTo(sdn);
|
|
||||||
createdSdn!.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
|
||||||
createdSdn.UpdatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
|
|
||||||
|
|
||||||
var updatedSdn = _sdnFaker.Generate();
|
|
||||||
updatedSdn.Id = sdn.Id;
|
|
||||||
|
|
||||||
await _sdnRepository.Upsert(updatedSdn);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
var result = await _context.Sdns.FindAsync(sdn.Id);
|
|
||||||
|
|
||||||
result.Should().BeEquivalentTo(updatedSdn);
|
|
||||||
result!.CreatedAt.Should().BeSameDateAs(createdSdn.CreatedAt);
|
|
||||||
result.UpdatedAt.Should().BeAfter(createdSdn.UpdatedAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
|
||||||
{
|
|
||||||
await _context.Database.MigrateAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisposeAsync()
|
|
||||||
{
|
|
||||||
await _context.DisposeAsync();
|
|
||||||
|
|
||||||
SqliteConnection.ClearAllPools();
|
|
||||||
|
|
||||||
File.Delete(_context.DatabasePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
namespace SanctionsSearch.Worker.Tests.Integration;
|
||||||
|
|
||||||
|
public class SdnRepositoryTests : IAsyncLifetime
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _context;
|
||||||
|
private readonly SdnRepository _sdnRepository;
|
||||||
|
private readonly SdnFaker _sdnFaker = new();
|
||||||
|
private readonly Mock<TimeProvider> _timeProvider = new();
|
||||||
|
|
||||||
|
public SdnRepositoryTests()
|
||||||
|
{
|
||||||
|
var loggerFactory = LoggerFactory.Create(builder => builder.ClearProviders());
|
||||||
|
var options = new DbOptions { DatabaseName = $"{Guid.NewGuid()}.db" };
|
||||||
|
|
||||||
|
_context = new AppDbContext(options, _timeProvider.Object);
|
||||||
|
_sdnRepository = new SdnRepository(_context, new Logger<SdnRepository>(loggerFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Upsert_WithNewEntity_ShouldAddEntityToDatabase()
|
||||||
|
{
|
||||||
|
var now = DateTimeOffset.UtcNow;
|
||||||
|
_timeProvider.Setup(x => x.GetUtcNow()).Returns(now);
|
||||||
|
|
||||||
|
var sdn = _sdnFaker.Generate();
|
||||||
|
|
||||||
|
await _sdnRepository.Upsert(sdn);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var result = await _context.Sdns.FindAsync(sdn.Id);
|
||||||
|
|
||||||
|
result.Should().BeEquivalentTo(sdn);
|
||||||
|
result!.CreatedAt.Should().Be(now.DateTime);
|
||||||
|
result.UpdatedAt.Should().Be(now.DateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Upsert_WithExistingEntity_ShouldUpdateEntityInDatabase()
|
||||||
|
{
|
||||||
|
var createdTimeStamp = DateTimeOffset.UtcNow;
|
||||||
|
var updatedTimeStamp = createdTimeStamp.AddSeconds(2);
|
||||||
|
|
||||||
|
_timeProvider.Setup(x => x.GetUtcNow()).Returns(createdTimeStamp);
|
||||||
|
|
||||||
|
var sdn = _sdnFaker.Generate();
|
||||||
|
|
||||||
|
// Insert the entity
|
||||||
|
await _sdnRepository.Upsert(sdn);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Assert the entity was created
|
||||||
|
var createdSdn = await _context.Sdns.FindAsync(sdn.Id);
|
||||||
|
|
||||||
|
createdSdn.Should().BeEquivalentTo(sdn);
|
||||||
|
createdSdn!.CreatedAt.Should().Be(createdTimeStamp.DateTime);
|
||||||
|
createdSdn.UpdatedAt.Should().Be(createdTimeStamp.DateTime);
|
||||||
|
|
||||||
|
_timeProvider.Setup(x => x.GetUtcNow()).Returns(updatedTimeStamp);
|
||||||
|
|
||||||
|
// Update the entity
|
||||||
|
sdn.Name = "Updated";
|
||||||
|
await _sdnRepository.Upsert(sdn);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Assert the entity was updated
|
||||||
|
var result = await _context.Sdns.FindAsync(sdn.Id);
|
||||||
|
|
||||||
|
result.Should().BeEquivalentTo(sdn);
|
||||||
|
result!.CreatedAt.Should().BeSameDateAs(createdTimeStamp.DateTime);
|
||||||
|
result.UpdatedAt.Should().BeSameDateAs(updatedTimeStamp.DateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Find_WithPredicate_ShouldReturnEntitiesMatchingPredicate()
|
||||||
|
{
|
||||||
|
var sdn1 = _sdnFaker.Generate();
|
||||||
|
var sdn2 = _sdnFaker.Generate();
|
||||||
|
var sdn3 = _sdnFaker.Generate();
|
||||||
|
|
||||||
|
await _sdnRepository.Upsert(sdn1);
|
||||||
|
await _sdnRepository.Upsert(sdn2);
|
||||||
|
await _sdnRepository.Upsert(sdn3);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var result = await _sdnRepository.Find(x => x.Id == sdn2.Id);
|
||||||
|
|
||||||
|
result.Should().HaveCount(1);
|
||||||
|
result.First().Should().BeEquivalentTo(sdn2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await _context.Database.MigrateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DisposeAsync()
|
||||||
|
{
|
||||||
|
await _context.DisposeAsync();
|
||||||
|
|
||||||
|
SqliteConnection.ClearAllPools();
|
||||||
|
|
||||||
|
File.Delete(_context.DatabasePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
<CoverletOutput>./TestResults/coverage/</CoverletOutput>
|
<CoverletOutput>./TestResults/coverage/</CoverletOutput>
|
||||||
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
|
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
|
||||||
<Include>[SanctionsSearch.Worker]*</Include>
|
<Include>[SanctionsSearch.Worker]*</Include>
|
||||||
|
<Exclude>[SanctionsSearch.Worker]SanctionsSearch.Worker.Migrations*</Exclude>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target Name="GenerateHtmlCoverageReport" AfterTargets="GenerateCoverageResultAfterTest">
|
<Target Name="GenerateHtmlCoverageReport" AfterTargets="GenerateCoverageResultAfterTest">
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SanctionsSearch.Worker.Tests.Unit;
|
namespace SanctionsSearch.Worker.Tests.Unit;
|
||||||
|
|
||||||
public class OfacFileServiceTests
|
public class OfacFileServiceTests
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
namespace SanctionsSearch.Worker.Tests.Unit;
|
||||||
|
|
||||||
|
public class SdnRepositoryTests
|
||||||
|
{
|
||||||
|
private readonly Mock<DbContext> _context = new();
|
||||||
|
private readonly Mock<ILogger<SdnRepository>> _logger = new();
|
||||||
|
private readonly SdnFaker _sdnFaker = new();
|
||||||
|
private readonly SdnRepository _sdnRepository;
|
||||||
|
|
||||||
|
public SdnRepositoryTests()
|
||||||
|
{
|
||||||
|
_sdnRepository = new SdnRepository(_context.Object, _logger.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Upsert_WhenExceptionIsThrown_ItShouldLogError()
|
||||||
|
{
|
||||||
|
var sdn = _sdnFaker.Generate();
|
||||||
|
var mockSet = new Mock<DbSet<Sdn>>();
|
||||||
|
|
||||||
|
mockSet
|
||||||
|
.Setup(x => x.FindAsync(sdn.Id))
|
||||||
|
.Throws<Exception>();
|
||||||
|
|
||||||
|
_context
|
||||||
|
.Setup(x => x.Set<Sdn>())
|
||||||
|
.Returns(mockSet.Object);
|
||||||
|
|
||||||
|
await _sdnRepository.Upsert(sdn);
|
||||||
|
|
||||||
|
_logger.Verify(
|
||||||
|
x => x.Log(
|
||||||
|
LogLevel.Error,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.IsAny<It.IsAnyType>(),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Find_WhenExceptionIsThrown_ItShouldReturnEmptyListAndLogError()
|
||||||
|
{
|
||||||
|
_context
|
||||||
|
.Setup(x => x.Set<Sdn>())
|
||||||
|
.Throws<Exception>();
|
||||||
|
|
||||||
|
var result = await _sdnRepository.Find(x => x.Id == 1);
|
||||||
|
|
||||||
|
result.Should().BeEmpty();
|
||||||
|
|
||||||
|
_logger.Verify(
|
||||||
|
x => x.Log(
|
||||||
|
LogLevel.Error,
|
||||||
|
It.IsAny<EventId>(),
|
||||||
|
It.IsAny<It.IsAnyType>(),
|
||||||
|
It.IsAny<Exception>(),
|
||||||
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
|
global using System.Net;
|
||||||
|
global using System.Text;
|
||||||
|
|
||||||
|
global using Bogus;
|
||||||
|
|
||||||
global using FluentAssertions;
|
global using FluentAssertions;
|
||||||
|
|
||||||
|
global using Microsoft.Data.Sqlite;
|
||||||
|
global using Microsoft.EntityFrameworkCore;
|
||||||
global using Microsoft.Extensions.Logging;
|
global using Microsoft.Extensions.Logging;
|
||||||
global using Microsoft.Extensions.Options;
|
global using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
@@ -7,7 +14,8 @@ global using Moq;
|
|||||||
|
|
||||||
global using RichardSzalay.MockHttp;
|
global using RichardSzalay.MockHttp;
|
||||||
|
|
||||||
global using SanctionsSearch.Worker.Services;
|
|
||||||
global using SanctionsSearch.Worker.Options;
|
|
||||||
global using SanctionsSearch.Worker.Models;
|
global using SanctionsSearch.Worker.Models;
|
||||||
global using SanctionsSearch.Worker.Persistence;
|
global using SanctionsSearch.Worker.Options;
|
||||||
|
global using SanctionsSearch.Worker.Persistence;
|
||||||
|
global using SanctionsSearch.Worker.Services;
|
||||||
|
global using SanctionsSearch.Worker.Tests.Fakes;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using FluentResults;
|
|
||||||
|
|
||||||
namespace SanctionsSearch.Worker.Interfaces;
|
namespace SanctionsSearch.Worker.Interfaces;
|
||||||
|
|
||||||
interface IOfacFileService
|
interface IOfacFileService
|
||||||
@@ -12,4 +10,4 @@ interface IOfacFileService
|
|||||||
Task<Result<Stream>> GetConAddressesFileAsync();
|
Task<Result<Stream>> GetConAddressesFileAsync();
|
||||||
Task<Result<Stream>> GetConAltNamesFileAsync();
|
Task<Result<Stream>> GetConAltNamesFileAsync();
|
||||||
Task<Result<Stream>> GetConCommentsFileAsync();
|
Task<Result<Stream>> GetConCommentsFileAsync();
|
||||||
}
|
}
|
||||||
@@ -4,4 +4,4 @@ interface IRepository<T> where T : Entity
|
|||||||
{
|
{
|
||||||
Task Upsert(T entity);
|
Task Upsert(T entity);
|
||||||
Task<IEnumerable<T>> Find(Expression<Func<T, bool>> predicate);
|
Task<IEnumerable<T>> Find(Expression<Func<T, bool>> predicate);
|
||||||
}
|
}
|
||||||
@@ -4,130 +4,130 @@
|
|||||||
|
|
||||||
namespace SanctionsSearch.Worker.Migrations
|
namespace SanctionsSearch.Worker.Migrations
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class initial_migration : Migration
|
||||||
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class initial_migration : Migration
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
migrationBuilder.CreateTable(
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
name: "Sdns",
|
||||||
{
|
columns: table => new
|
||||||
migrationBuilder.CreateTable(
|
{
|
||||||
name: "Sdns",
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
columns: table => new
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
{
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
Type = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
Program = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
Title = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Type = table.Column<string>(type: "TEXT", nullable: false),
|
CallSign = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Program = table.Column<string>(type: "TEXT", nullable: false),
|
VesselType = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Title = table.Column<string>(type: "TEXT", nullable: false),
|
Tonnage = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
CallSign = table.Column<string>(type: "TEXT", nullable: false),
|
GrossRegisteredTonnage = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
VesselType = table.Column<string>(type: "TEXT", nullable: false),
|
VesselFlag = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Tonnage = table.Column<string>(type: "TEXT", nullable: false),
|
VesselOwner = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
GrossRegisteredTonnage = table.Column<string>(type: "TEXT", nullable: false),
|
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
VesselFlag = table.Column<string>(type: "TEXT", nullable: false),
|
},
|
||||||
VesselOwner = table.Column<string>(type: "TEXT", nullable: false),
|
constraints: table =>
|
||||||
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
{
|
||||||
},
|
table.PrimaryKey("PK_Sdns", x => x.Id);
|
||||||
constraints: table =>
|
});
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Sdns", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Addresses",
|
name: "Addresses",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
|
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
StreetAddress = table.Column<string>(type: "TEXT", nullable: false),
|
StreetAddress = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
CityProvincePostal = table.Column<string>(type: "TEXT", nullable: false),
|
CityProvincePostal = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Country = table.Column<string>(type: "TEXT", nullable: false),
|
Country = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Addresses", x => x.Id);
|
table.PrimaryKey("PK_Addresses", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Addresses_Sdns_SdnId",
|
name: "FK_Addresses_Sdns_SdnId",
|
||||||
column: x => x.SdnId,
|
column: x => x.SdnId,
|
||||||
principalTable: "Sdns",
|
principalTable: "Sdns",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Aliases",
|
name: "Aliases",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
|
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
Type = table.Column<string>(type: "TEXT", nullable: false),
|
Type = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Aliases", x => x.Id);
|
table.PrimaryKey("PK_Aliases", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Aliases_Sdns_SdnId",
|
name: "FK_Aliases_Sdns_SdnId",
|
||||||
column: x => x.SdnId,
|
column: x => x.SdnId,
|
||||||
principalTable: "Sdns",
|
principalTable: "Sdns",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Comments",
|
name: "Comments",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
|
SdnId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
Remarks = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Comments", x => x.Id);
|
table.PrimaryKey("PK_Comments", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Comments_Sdns_SdnId",
|
name: "FK_Comments_Sdns_SdnId",
|
||||||
column: x => x.SdnId,
|
column: x => x.SdnId,
|
||||||
principalTable: "Sdns",
|
principalTable: "Sdns",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Addresses_SdnId",
|
name: "IX_Addresses_SdnId",
|
||||||
table: "Addresses",
|
table: "Addresses",
|
||||||
column: "SdnId");
|
column: "SdnId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Aliases_SdnId",
|
name: "IX_Aliases_SdnId",
|
||||||
table: "Aliases",
|
table: "Aliases",
|
||||||
column: "SdnId");
|
column: "SdnId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Comments_SdnId",
|
name: "IX_Comments_SdnId",
|
||||||
table: "Comments",
|
table: "Comments",
|
||||||
column: "SdnId");
|
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Addresses");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Aliases");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Comments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Sdns");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
-90
@@ -1,107 +1,108 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace SanctionsSearch.Worker.Migrations
|
namespace SanctionsSearch.Worker.Migrations
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class update_models_with_timestamps : Migration
|
||||||
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class update_models_with_timestamps : Migration
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
name: "CreatedAt",
|
||||||
{
|
table: "Sdns",
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
type: "TEXT",
|
||||||
name: "CreatedAt",
|
nullable: false,
|
||||||
table: "Sdns",
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
type: "TEXT",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "UpdatedAt",
|
name: "UpdatedAt",
|
||||||
table: "Sdns",
|
table: "Sdns",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "CreatedAt",
|
name: "CreatedAt",
|
||||||
table: "Comments",
|
table: "Comments",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "UpdatedAt",
|
name: "UpdatedAt",
|
||||||
table: "Comments",
|
table: "Comments",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "CreatedAt",
|
name: "CreatedAt",
|
||||||
table: "Aliases",
|
table: "Aliases",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "UpdatedAt",
|
name: "UpdatedAt",
|
||||||
table: "Aliases",
|
table: "Aliases",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "CreatedAt",
|
name: "CreatedAt",
|
||||||
table: "Addresses",
|
table: "Addresses",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
name: "UpdatedAt",
|
name: "UpdatedAt",
|
||||||
table: "Addresses",
|
table: "Addresses",
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "CreatedAt",
|
|
||||||
table: "Sdns");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "UpdatedAt",
|
|
||||||
table: "Sdns");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "CreatedAt",
|
|
||||||
table: "Comments");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "UpdatedAt",
|
|
||||||
table: "Comments");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "CreatedAt",
|
|
||||||
table: "Aliases");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "UpdatedAt",
|
|
||||||
table: "Aliases");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "CreatedAt",
|
|
||||||
table: "Addresses");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "UpdatedAt",
|
|
||||||
table: "Addresses");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CreatedAt",
|
||||||
|
table: "Sdns");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UpdatedAt",
|
||||||
|
table: "Sdns");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CreatedAt",
|
||||||
|
table: "Comments");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UpdatedAt",
|
||||||
|
table: "Comments");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CreatedAt",
|
||||||
|
table: "Aliases");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UpdatedAt",
|
||||||
|
table: "Aliases");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CreatedAt",
|
||||||
|
table: "Addresses");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UpdatedAt",
|
||||||
|
table: "Addresses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace SanctionsSearch.Worker.Persistence;
|
namespace SanctionsSearch.Worker.Persistence;
|
||||||
|
|
||||||
class AppDbContext(DbOptions options) : DbContext
|
class AppDbContext(DbOptions options, TimeProvider timeProvider) : DbContext
|
||||||
{
|
{
|
||||||
private readonly DbOptions _options = options;
|
private readonly DbOptions _options = options;
|
||||||
|
private readonly TimeProvider _timeProvider = timeProvider;
|
||||||
private SqliteConnection? _connection;
|
private SqliteConnection? _connection;
|
||||||
internal string DatabasePath { get; private set; } = string.Empty;
|
internal string DatabasePath { get; private set; } = string.Empty;
|
||||||
public DbSet<Sdn> Sdns { get; set; } = default!;
|
public DbSet<Sdn> Sdns { get; set; } = default!;
|
||||||
@@ -32,8 +33,7 @@ class AppDbContext(DbOptions options) : DbContext
|
|||||||
|
|
||||||
public async override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
public async override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// TODO: Use TimeProvider instead of DateTime.UtcNow
|
var now = _timeProvider.GetUtcNow().DateTime;
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
|
|
||||||
foreach (var changedEntity in ChangeTracker.Entries())
|
foreach (var changedEntity in ChangeTracker.Entries())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ namespace SanctionsSearch.Worker.Persistence;
|
|||||||
|
|
||||||
class EfRepository<T> : IRepository<T> where T : Entity
|
class EfRepository<T> : IRepository<T> where T : Entity
|
||||||
{
|
{
|
||||||
protected readonly AppDbContext _context;
|
protected readonly DbContext _context;
|
||||||
protected readonly ILogger<EfRepository<T>> _logger;
|
protected readonly ILogger<EfRepository<T>> _logger;
|
||||||
|
|
||||||
public EfRepository(AppDbContext context, ILogger<EfRepository<T>> logger)
|
public EfRepository(DbContext context, ILogger<EfRepository<T>> logger)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace SanctionsSearch.Worker.Persistence;
|
namespace SanctionsSearch.Worker.Persistence;
|
||||||
|
|
||||||
class SdnRepository(
|
class SdnRepository(
|
||||||
AppDbContext dbContext,
|
DbContext dbContext,
|
||||||
ILogger<SdnRepository> logger
|
ILogger<SdnRepository> logger
|
||||||
) : EfRepository<Sdn>(dbContext, logger), ISdnRepository
|
) : EfRepository<Sdn>(dbContext, logger), ISdnRepository
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,62 +1,71 @@
|
|||||||
using SanctionsSearch.Worker.Persistence;
|
namespace SanctionsSearch.Worker;
|
||||||
using SanctionsSearch.Worker.Setup;
|
|
||||||
|
|
||||||
if (EF.IsDesignTime)
|
class Program
|
||||||
{
|
{
|
||||||
Host.CreateDefaultBuilder().Build().Run();
|
async static Task Main(string[] args)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
|
||||||
.Enrich.WithProperty("Application", "SanctionsSearch.Worker")
|
|
||||||
.Enrich.WithEnvironmentName()
|
|
||||||
.Enrich.WithMachineName()
|
|
||||||
.Enrich.WithProcessId()
|
|
||||||
.Enrich.WithThreadId()
|
|
||||||
.Enrich.WithExceptionDetails()
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.MinimumLevel.Debug()
|
|
||||||
.WriteTo.Console()
|
|
||||||
.WriteTo.File(new CompactJsonFormatter(), "logs/log.json", rollingInterval: RollingInterval.Day)
|
|
||||||
.CreateLogger();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Information("Starting Sanctions Search worker");
|
|
||||||
|
|
||||||
var builder = Host.CreateApplicationBuilder(args);
|
|
||||||
|
|
||||||
builder.Logging.ClearProviders();
|
|
||||||
builder.Logging.AddSerilog();
|
|
||||||
|
|
||||||
builder.Services.ConfigureOptions<OfacFileServiceOptionsSetup>();
|
|
||||||
builder.Services.ConfigureOptions<DbOptionsSetup>();
|
|
||||||
builder.Services.AddScoped(rs => rs.GetRequiredService<IOptionsSnapshot<DbOptions>>().Value);
|
|
||||||
|
|
||||||
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");
|
if (EF.IsDesignTime) return;
|
||||||
await context.Database.MigrateAsync();
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.Enrich.WithProperty("Application", "SanctionsSearch.Worker")
|
||||||
|
.Enrich.WithEnvironmentName()
|
||||||
|
.Enrich.WithMachineName()
|
||||||
|
.Enrich.WithProcessId()
|
||||||
|
.Enrich.WithThreadId()
|
||||||
|
.Enrich.WithExceptionDetails()
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.MinimumLevel.Debug()
|
||||||
|
.WriteTo.Console()
|
||||||
|
.WriteTo.File(new CompactJsonFormatter(), "logs/log.json", rollingInterval: RollingInterval.Day)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Information("Starting Sanctions Search worker");
|
||||||
|
|
||||||
|
var builder = CreateHostBuilder(args);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
await host.RunAsync();
|
||||||
|
|
||||||
|
Log.Information("Stopping Sanctions Search worker");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Fatal(ex, "Worker terminated unexpectedly");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await Log.CloseAndFlushAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
host.Run();
|
static HostApplicationBuilder CreateHostBuilder(string[] args)
|
||||||
|
{
|
||||||
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
Log.Information("Stopping Sanctions Search worker");
|
builder.Logging.ClearProviders();
|
||||||
}
|
builder.Logging.AddSerilog();
|
||||||
catch (Exception ex)
|
|
||||||
{
|
builder.Services.ConfigureOptions<OfacFileServiceOptionsSetup>();
|
||||||
Log.Fatal(ex, "Worker terminated unexpectedly");
|
builder.Services.ConfigureOptions<DbOptionsSetup>();
|
||||||
}
|
builder.Services.AddScoped(rs => rs.GetRequiredService<IOptionsSnapshot<DbOptions>>().Value);
|
||||||
finally
|
|
||||||
{
|
builder.Services.AddSingleton(TimeProvider.System);
|
||||||
await Log.CloseAndFlushAsync();
|
builder.Services.AddDbContext<AppDbContext>();
|
||||||
|
builder.Services.AddHostedService<Worker>();
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
|
|
||||||
using FluentResults;
|
|
||||||
|
|
||||||
namespace SanctionsSearch.Worker.Services;
|
namespace SanctionsSearch.Worker.Services;
|
||||||
|
|
||||||
class OfacFileService(
|
class OfacFileService(
|
||||||
@@ -45,4 +42,4 @@ class OfacFileService(
|
|||||||
public Task<Result<Stream>> GetConAddressesFileAsync() => GetFileAsync(_options.GetConAddressesFileUri());
|
public Task<Result<Stream>> GetConAddressesFileAsync() => GetFileAsync(_options.GetConAddressesFileUri());
|
||||||
public Task<Result<Stream>> GetConAltNamesFileAsync() => GetFileAsync(_options.GetConAltNamesFileUri());
|
public Task<Result<Stream>> GetConAltNamesFileAsync() => GetFileAsync(_options.GetConAltNamesFileUri());
|
||||||
public Task<Result<Stream>> GetConCommentsFileAsync() => GetFileAsync(_options.GetConCommentsFileUri());
|
public Task<Result<Stream>> GetConCommentsFileAsync() => GetFileAsync(_options.GetConCommentsFileUri());
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
|
global using System.Linq.Expressions;
|
||||||
|
global using System.Reflection;
|
||||||
|
|
||||||
|
global using FluentResults;
|
||||||
|
|
||||||
|
global using Microsoft.Data.Sqlite;
|
||||||
|
global using Microsoft.EntityFrameworkCore;
|
||||||
global using Microsoft.Extensions.Options;
|
global using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
global using SanctionsSearch.Worker.Interfaces;
|
global using SanctionsSearch.Worker.Interfaces;
|
||||||
global using SanctionsSearch.Worker.Options;
|
|
||||||
global using SanctionsSearch.Worker;
|
|
||||||
global using SanctionsSearch.Worker.Models;
|
global using SanctionsSearch.Worker.Models;
|
||||||
|
global using SanctionsSearch.Worker.Options;
|
||||||
|
global using SanctionsSearch.Worker.Persistence;
|
||||||
|
global using SanctionsSearch.Worker.Services;
|
||||||
|
global using SanctionsSearch.Worker.Setup;
|
||||||
|
|
||||||
global using Serilog;
|
global using Serilog;
|
||||||
global using Serilog.Exceptions;
|
global using Serilog.Exceptions;
|
||||||
global using Serilog.Formatting.Compact;
|
global using Serilog.Formatting.Compact;
|
||||||
|
|
||||||
global using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
global using System.Linq.Expressions;
|
|
||||||
|
|
||||||
global using System.Reflection;
|
|
||||||
|
|
||||||
global using Microsoft.Data.Sqlite;
|
|
||||||
Reference in New Issue
Block a user