Use local dotnet-ef tooling and strict EF migration checks
This commit is contained in:
@@ -13,4 +13,6 @@ Also see the other related technical documentation: TECH.md, REQUIREMENTS.md and
|
|||||||
- After every iteration, do a git commit with a brief summary of the changes as a commit message.
|
- After every iteration, do a git commit with a brief summary of the changes as a commit message.
|
||||||
- Keep changes small and commit often. If one iteration encompasses many smaller tasks with more than one commit, create a git branch and do the commits there. Let me review the branch before merging it back to master.
|
- Keep changes small and commit often. If one iteration encompasses many smaller tasks with more than one commit, create a git branch and do the commits there. Let me review the branch before merging it back to master.
|
||||||
- If you find unexpected changes in the code (deletions, changes, diff results that were not communicated), never revert them and never restore the old state. Assume that those changes happened with intent.
|
- If you find unexpected changes in the code (deletions, changes, diff results that were not communicated), never revert them and never restore the old state. Assume that those changes happened with intent.
|
||||||
|
- Never use `git restore`, `git checkout --`, reset commands, or equivalent rollback actions to discard local changes unless the user explicitly asks for that exact rollback.
|
||||||
|
- If a required tool is missing (for example `dotnet-ef`), install/configure the tool (prefer repo-local setup such as `dotnet tool manifest`) instead of weakening validations or muting warnings. If installation is blocked, stop and ask before changing validation strictness.
|
||||||
- After changing the database, if your build is blocked by a running dotnet process, feel free to kill the process and retry the operation once.
|
- After changing the database, if your build is blocked by a running dotnet process, feel free to kill the process and retry the operation once.
|
||||||
|
|||||||
9
FAQ.md
9
FAQ.md
@@ -30,6 +30,15 @@ To start with a clean backend state, stop the app and remove the corresponding S
|
|||||||
|
|
||||||
Usually no. Startup now uses EF Core migration-based schema upgrades (`Database.Migrate`) and applies pending migrations automatically.
|
Usually no. Startup now uses EF Core migration-based schema upgrades (`Database.Migrate`) and applies pending migrations automatically.
|
||||||
|
|
||||||
|
## How do I add a new EF migration in this repo?
|
||||||
|
|
||||||
|
Use the repo-local tool manifest:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet tool restore
|
||||||
|
dotnet dotnet-ef migrations add <MigrationName> --project RpgRoller/RpgRoller.csproj --startup-project RpgRoller/RpgRoller.csproj
|
||||||
|
```
|
||||||
|
|
||||||
## Does the backend read SQLite on every API call?
|
## Does the backend read SQLite on every API call?
|
||||||
|
|
||||||
No. The backend loads state from SQLite once during startup into in-memory state and serves requests from memory. Successful state mutations are then written back to SQLite.
|
No. The backend loads state from SQLite once during startup into in-memory state and serves requests from memory. Successful state mutations are then written back to SQLite.
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ Backend state persistence:
|
|||||||
|
|
||||||
- .NET SDK 10.0+
|
- .NET SDK 10.0+
|
||||||
- PowerShell 7+
|
- PowerShell 7+
|
||||||
|
- Run `dotnet tool restore` once to enable the repo-local `dotnet-ef` command.
|
||||||
|
|
||||||
## Local Development
|
## Local Development
|
||||||
|
|
||||||
@@ -63,6 +64,12 @@ VS Code F5 debug profiles are available in `.vscode/launch.json`:
|
|||||||
|
|
||||||
To use a custom SQLite database path, set `ConnectionStrings__RpgRoller`.
|
To use a custom SQLite database path, set `ConnectionStrings__RpgRoller`.
|
||||||
|
|
||||||
|
For migration authoring, use the local tool command form:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet dotnet-ef migrations add <MigrationName> --project RpgRoller/RpgRoller.csproj --startup-project RpgRoller/RpgRoller.csproj
|
||||||
|
```
|
||||||
|
|
||||||
## Frontend Runtime
|
## Frontend Runtime
|
||||||
|
|
||||||
- Runtime frontend is Blazor Server with interactive components.
|
- Runtime frontend is Blazor Server with interactive components.
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
using RpgRoller.Hosting;
|
using RpgRoller.Hosting;
|
||||||
using RpgRoller.Data;
|
using RpgRoller.Data;
|
||||||
|
|
||||||
@@ -98,7 +97,6 @@ public sealed class HostingCoverageTests
|
|||||||
|
|
||||||
var options = new DbContextOptionsBuilder<RpgRollerDbContext>()
|
var options = new DbContextOptionsBuilder<RpgRollerDbContext>()
|
||||||
.UseSqlite(connectionString)
|
.UseSqlite(connectionString)
|
||||||
.ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning))
|
|
||||||
.Options;
|
.Options;
|
||||||
|
|
||||||
using (var db = new RpgRollerDbContext(options))
|
using (var db = new RpgRollerDbContext(options))
|
||||||
@@ -125,5 +123,10 @@ public sealed class HostingCoverageTests
|
|||||||
historyCommand.CommandText = "SELECT COUNT(*) FROM \"__EFMigrationsHistory\" WHERE \"MigrationId\" = '20260226084000_InitialSchema';";
|
historyCommand.CommandText = "SELECT COUNT(*) FROM \"__EFMigrationsHistory\" WHERE \"MigrationId\" = '20260226084000_InitialSchema';";
|
||||||
var historyCount = Convert.ToInt32(historyCommand.ExecuteScalar());
|
var historyCount = Convert.ToInt32(historyCommand.ExecuteScalar());
|
||||||
Assert.Equal(1, historyCount);
|
Assert.Equal(1, historyCount);
|
||||||
|
|
||||||
|
using var modelSyncHistoryCommand = verifyConnection.CreateCommand();
|
||||||
|
modelSyncHistoryCommand.CommandText = "SELECT COUNT(*) FROM \"__EFMigrationsHistory\" WHERE \"MigrationId\" = '20260226090000_ModelSync';";
|
||||||
|
var modelSyncHistoryCount = Convert.ToInt32(modelSyncHistoryCommand.ExecuteScalar());
|
||||||
|
Assert.Equal(1, modelSyncHistoryCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System.Net;
|
|||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using Microsoft.AspNetCore.Mvc.Testing;
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using RpgRoller.Contracts;
|
using RpgRoller.Contracts;
|
||||||
@@ -34,8 +33,7 @@ public abstract class ApiTestBase : IClassFixture<WebApplicationFactory<Program>
|
|||||||
|
|
||||||
var dbPath = Path.Combine(Path.GetTempPath(), $"rpgroller-tests-{Guid.NewGuid():N}.db");
|
var dbPath = Path.Combine(Path.GetTempPath(), $"rpgroller-tests-{Guid.NewGuid():N}.db");
|
||||||
services.AddDbContextFactory<RpgRollerDbContext>(options =>
|
services.AddDbContextFactory<RpgRollerDbContext>(options =>
|
||||||
options.UseSqlite($"Data Source={dbPath}")
|
options.UseSqlite($"Data Source={dbPath}"));
|
||||||
.ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)));
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
using RpgRoller.Data;
|
using RpgRoller.Data;
|
||||||
using RpgRoller.Domain;
|
using RpgRoller.Domain;
|
||||||
using RpgRoller.Services;
|
using RpgRoller.Services;
|
||||||
@@ -20,8 +19,7 @@ public static class ServiceCollectionExtensions
|
|||||||
|
|
||||||
services.AddSingleton<IPasswordHasher<UserAccount>, PasswordHasher<UserAccount>>();
|
services.AddSingleton<IPasswordHasher<UserAccount>, PasswordHasher<UserAccount>>();
|
||||||
services.AddDbContextFactory<RpgRollerDbContext>(options =>
|
services.AddDbContextFactory<RpgRollerDbContext>(options =>
|
||||||
options.UseSqlite(sqliteConnectionString)
|
options.UseSqlite(sqliteConnectionString));
|
||||||
.ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning)));
|
|
||||||
services.AddSingleton<IDiceRoller, RandomDiceRoller>();
|
services.AddSingleton<IDiceRoller, RandomDiceRoller>();
|
||||||
services.AddSingleton<IGameService, GameService>();
|
services.AddSingleton<IGameService, GameService>();
|
||||||
|
|
||||||
|
|||||||
212
RpgRoller/Migrations/20260226075224_20260226090000_ModelSync.Designer.cs
generated
Normal file
212
RpgRoller/Migrations/20260226075224_20260226090000_ModelSync.Designer.cs
generated
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using RpgRoller.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RpgRoller.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(RpgRollerDbContext))]
|
||||||
|
[Migration("20260226090000_ModelSync")]
|
||||||
|
partial class _20260226090000_ModelSync
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("RpgRoller.Domain.Campaign", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("GmUserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Ruleset")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("Version")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GmUserId");
|
||||||
|
|
||||||
|
b.ToTable("Campaigns");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RpgRoller.Domain.Character", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("CampaignId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("OwnerUserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CampaignId");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerUserId");
|
||||||
|
|
||||||
|
b.ToTable("Characters");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RpgRoller.Domain.RollLogEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Breakdown")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("CampaignId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Result")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("RollerUserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("SkillId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("TimestampUtc")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Visibility")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CampaignId");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.HasIndex("RollerUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SkillId");
|
||||||
|
|
||||||
|
b.ToTable("RollLogEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RpgRoller.Domain.Skill", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowFumble")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DiceRollDefinition")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("WildDice")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.ToTable("Skills");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RpgRoller.Domain.UserAccount", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ActiveCharacterId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UsernameNormalized")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UsernameNormalized")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RpgRoller.Domain.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Token");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Sessions");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RpgRoller.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class _20260226090000_ModelSync : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
// No-op migration generated to sync EF snapshot to existing schema.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
// Intentionally empty.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,78 +1,209 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using RpgRoller.Data;
|
using RpgRoller.Data;
|
||||||
using RpgRoller.Domain;
|
|
||||||
|
|
||||||
namespace RpgRoller.Migrations;
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RpgRoller.Migrations
|
||||||
|
{
|
||||||
[DbContext(typeof(RpgRollerDbContext))]
|
[DbContext(typeof(RpgRollerDbContext))]
|
||||||
internal sealed class RpgRollerDbContextModelSnapshot : ModelSnapshot
|
partial class RpgRollerDbContextModelSnapshot : ModelSnapshot
|
||||||
{
|
{
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.2");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.2");
|
||||||
|
|
||||||
modelBuilder.Entity<Campaign>(entity =>
|
modelBuilder.Entity("RpgRoller.Domain.Campaign", b =>
|
||||||
{
|
{
|
||||||
entity.HasKey(x => x.Id);
|
b.Property<Guid>("Id")
|
||||||
entity.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
.ValueGeneratedOnAdd()
|
||||||
entity.Property(x => x.Ruleset).HasConversion<string>().IsRequired();
|
.HasColumnType("TEXT");
|
||||||
entity.Property(x => x.Version).IsRequired();
|
|
||||||
entity.HasIndex(x => x.GmUserId);
|
b.Property<Guid>("GmUserId")
|
||||||
entity.ToTable("Campaigns");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Ruleset")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("Version")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GmUserId");
|
||||||
|
|
||||||
|
b.ToTable("Campaigns");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<Character>(entity =>
|
modelBuilder.Entity("RpgRoller.Domain.Character", b =>
|
||||||
{
|
{
|
||||||
entity.HasKey(x => x.Id);
|
b.Property<Guid>("Id")
|
||||||
entity.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
.ValueGeneratedOnAdd()
|
||||||
entity.HasIndex(x => x.OwnerUserId);
|
.HasColumnType("TEXT");
|
||||||
entity.HasIndex(x => x.CampaignId);
|
|
||||||
entity.ToTable("Characters");
|
b.Property<Guid>("CampaignId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("OwnerUserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CampaignId");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerUserId");
|
||||||
|
|
||||||
|
b.ToTable("Characters");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<RollLogEntry>(entity =>
|
modelBuilder.Entity("RpgRoller.Domain.RollLogEntry", b =>
|
||||||
{
|
{
|
||||||
entity.HasKey(x => x.Id);
|
b.Property<Guid>("Id")
|
||||||
entity.Property(x => x.Visibility).HasConversion<string>().IsRequired();
|
.ValueGeneratedOnAdd()
|
||||||
entity.Property(x => x.Breakdown).IsRequired().HasMaxLength(256);
|
.HasColumnType("TEXT");
|
||||||
entity.Property(x => x.TimestampUtc).IsRequired();
|
|
||||||
entity.HasIndex(x => x.CampaignId);
|
b.Property<string>("Breakdown")
|
||||||
entity.HasIndex(x => x.RollerUserId);
|
.IsRequired()
|
||||||
entity.HasIndex(x => x.SkillId);
|
.HasMaxLength(256)
|
||||||
entity.HasIndex(x => x.CharacterId);
|
.HasColumnType("TEXT");
|
||||||
entity.ToTable("RollLogEntries");
|
|
||||||
|
b.Property<Guid>("CampaignId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Result")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("RollerUserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("SkillId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("TimestampUtc")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Visibility")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CampaignId");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.HasIndex("RollerUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SkillId");
|
||||||
|
|
||||||
|
b.ToTable("RollLogEntries");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<Skill>(entity =>
|
modelBuilder.Entity("RpgRoller.Domain.Skill", b =>
|
||||||
{
|
{
|
||||||
entity.HasKey(x => x.Id);
|
b.Property<Guid>("Id")
|
||||||
entity.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
.ValueGeneratedOnAdd()
|
||||||
entity.Property(x => x.DiceRollDefinition).IsRequired().HasMaxLength(128);
|
.HasColumnType("TEXT");
|
||||||
entity.Property(x => x.WildDice).IsRequired();
|
|
||||||
entity.Property(x => x.AllowFumble).IsRequired();
|
b.Property<bool>("AllowFumble")
|
||||||
entity.HasIndex(x => x.CharacterId);
|
.HasColumnType("INTEGER");
|
||||||
entity.ToTable("Skills");
|
|
||||||
|
b.Property<Guid>("CharacterId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DiceRollDefinition")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("WildDice")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CharacterId");
|
||||||
|
|
||||||
|
b.ToTable("Skills");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<UserAccount>(entity =>
|
modelBuilder.Entity("RpgRoller.Domain.UserAccount", b =>
|
||||||
{
|
{
|
||||||
entity.HasKey(x => x.Id);
|
b.Property<Guid>("Id")
|
||||||
entity.Property(x => x.Username).IsRequired().HasMaxLength(64);
|
.ValueGeneratedOnAdd()
|
||||||
entity.Property(x => x.UsernameNormalized).IsRequired().HasMaxLength(64);
|
.HasColumnType("TEXT");
|
||||||
entity.Property(x => x.PasswordHash).IsRequired();
|
|
||||||
entity.Property(x => x.DisplayName).IsRequired().HasMaxLength(128);
|
b.Property<Guid?>("ActiveCharacterId")
|
||||||
entity.HasIndex(x => x.UsernameNormalized).IsUnique();
|
.HasColumnType("TEXT");
|
||||||
entity.ToTable("Users");
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UsernameNormalized")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UsernameNormalized")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<UserSession>(entity =>
|
modelBuilder.Entity("RpgRoller.Domain.UserSession", b =>
|
||||||
{
|
{
|
||||||
entity.HasKey(x => x.Token);
|
b.Property<string>("Token")
|
||||||
entity.Property(x => x.Token).HasMaxLength(64);
|
.HasMaxLength(64)
|
||||||
entity.Property(x => x.CreatedAtUtc).IsRequired();
|
.HasColumnType("TEXT");
|
||||||
entity.HasIndex(x => x.UserId);
|
|
||||||
entity.ToTable("Sessions");
|
b.Property<DateTimeOffset>("CreatedAtUtc")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Token");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Sessions");
|
||||||
});
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
13
dotnet-tools.json
Normal file
13
dotnet-tools.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"dotnet-ef": {
|
||||||
|
"version": "10.0.3",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-ef"
|
||||||
|
],
|
||||||
|
"rollForward": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user