Files
jdescopingtool/DOCUMENTATION/Architecture/Database.md
T
Joseph Doherty 26ff8d9b4f Initial commit: JDE Scoping Tool migration project
Set up repository with legacy .NET Framework 4.8 source (OLD/),
new .NET 10 Blazor solution (NEW/), OpenSpec specifications,
documentation, and project configuration.
2026-01-02 07:43:29 -05:00

203 lines
5.8 KiB
Markdown

# Database
The application uses SQL Server for the local cache database. Schema is managed using DbUp, with versioned SQL scripts embedded in the application.
## DbUp Overview
DbUp is a .NET library for deploying changes to SQL Server databases. It tracks which scripts have been executed in a `SchemaVersions` table and runs new scripts in alphabetical order.
Key benefits:
- Schema defined as code (versioned SQL scripts)
- Automatic migration on startup
- Idempotent - safe to run multiple times
- Simple, well-tested library
## Project Structure
```
JdeScoping.Database/
├── JdeScoping.Database.csproj
├── DatabaseMigrator.cs # Entry point for migrations
└── Scripts/
├── 001_CreateSearchTable.sql
├── 002_CreateDataUpdateTable.sql
├── 003_CreateWorkOrderTables.sql
├── 004_CreateLotTables.sql
├── 005_CreateReferenceTables.sql
└── ...
```
## Script Naming Convention
Scripts are named with a numeric prefix for ordering:
```
NNN_DescriptiveName.sql
```
- `NNN`: Zero-padded number (001, 002, etc.)
- `DescriptiveName`: Brief description of what the script does
- Scripts run in alphabetical order (numeric prefix ensures correct order)
## DatabaseMigrator Implementation
```csharp
using DbUp;
using Microsoft.Extensions.Configuration;
namespace JdeScoping.Database;
public class DatabaseMigrator
{
private readonly string _connectionString;
public DatabaseMigrator(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("SqlServer")
?? throw new InvalidOperationException("SqlServer connection string not configured");
}
public DatabaseUpgradeResult Migrate()
{
EnsureDatabase.For.SqlDatabase(_connectionString);
var upgrader = DeployChanges.To
.SqlDatabase(_connectionString)
.WithScriptsEmbeddedInAssembly(typeof(DatabaseMigrator).Assembly)
.WithTransaction()
.LogToConsole()
.Build();
return upgrader.PerformUpgrade();
}
}
```
## Embedding Scripts as Resources
Scripts are embedded in the assembly by configuring the project file:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Scripts\*.sql" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="dbup-sqlserver" Version="5.*" />
</ItemGroup>
</Project>
```
## Running Migrations on Startup
Migrations run early in application startup, before other services are configured:
```csharp
// In Program.cs
var builder = WebApplication.CreateBuilder(args);
// Run database migrations first
var migrator = new DatabaseMigrator(builder.Configuration);
var result = migrator.Migrate();
if (!result.Successful)
{
Console.WriteLine($"Database migration failed: {result.Error}");
return 1;
}
// Continue with normal startup...
builder.Host.UseWindowsService();
```
## Core Tables
The scoping tool cache database includes these primary tables:
| Table | Purpose |
|-------|---------|
| `Search` | User search requests, status, and results (Excel as VARBINARY) |
| `DataUpdate` | Tracks last sync timestamp per data type |
| `WorkOrder_Curr` | Current work orders from JDE |
| `WorkOrder_Hist` | Historical work orders from JDE |
| `LotUsage_Curr` | Current lot usage from CMS |
| `LotUsage_Hist` | Historical lot usage from CMS |
| `Lot` | Lot reference data |
| `Item` | Item master reference data |
| `WorkCenter` | Work center reference data |
| `JdeUser` | Operator reference data |
| `ProfitCenter` | Profit center reference data |
| `SchemaVersions` | DbUp tracking table (auto-created) |
## Example Migration Scripts
### 001_CreateSearchTable.sql
```sql
CREATE TABLE [dbo].[Search] (
[Id] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[UserId] NVARCHAR(50) NOT NULL,
[UserDisplayName] NVARCHAR(100) NULL,
[Criteria] NVARCHAR(MAX) NOT NULL,
[Status] INT NOT NULL DEFAULT 0,
[CreatedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
[StartedAt] DATETIME2 NULL,
[CompletedAt] DATETIME2 NULL,
[ResultCount] INT NULL,
[Results] VARBINARY(MAX) NULL,
[ErrorMessage] NVARCHAR(MAX) NULL
);
CREATE INDEX [IX_Search_Status] ON [dbo].[Search] ([Status]);
CREATE INDEX [IX_Search_UserId] ON [dbo].[Search] ([UserId]);
```
### 002_CreateDataUpdateTable.sql
```sql
CREATE TABLE [dbo].[DataUpdate] (
[Id] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[TableName] NVARCHAR(100) NOT NULL,
[UpdateType] NVARCHAR(20) NOT NULL,
[LastUpdated] DATETIME2 NOT NULL,
[RecordCount] INT NULL,
[Status] NVARCHAR(20) NOT NULL DEFAULT 'Completed'
);
CREATE UNIQUE INDEX [IX_DataUpdate_TableName_Type]
ON [dbo].[DataUpdate] ([TableName], [UpdateType]);
```
## Development vs Production
The same migration scripts run in all environments. For development with file-based data sources, the cache tables are still created but populated from JSON/CSV files instead of Oracle.
## Adding New Migrations
1. Create a new SQL file with the next number prefix
2. Write idempotent SQL (use `IF NOT EXISTS` where appropriate)
3. Build and run - DbUp picks up new embedded scripts automatically
```sql
-- Example: 006_AddNewColumn.sql
IF NOT EXISTS (
SELECT 1 FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.Search') AND name = 'Priority'
)
BEGIN
ALTER TABLE [dbo].[Search] ADD [Priority] INT NOT NULL DEFAULT 0;
END
```
## Related Documentation
- [Overview](./Overview.md)
- [Solution Structure](./SolutionStructure.md)
- [Configuration](./Configuration.md)
- [Data Flow](./DataFlow.md)