This commit is contained in:
Jöran Malek 2024-04-05 12:31:34 +02:00
parent 4c2b5cca93
commit 43b4d50e43
28 changed files with 674 additions and 249 deletions

View file

@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-ef": { "dotnet-ef": {
"version": "8.0.2", "version": "8.0.3",
"commands": [ "commands": [
"dotnet-ef" "dotnet-ef"
] ]

View file

@ -10,8 +10,8 @@
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.10" /> <PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.10" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.0.10" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.0.10" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.0.10" /> <PackageVersion Include="Avalonia.Themes.Fluent" Version="11.0.10" />
<PackageVersion Include="Dock.Avalonia" Version="11.0.0.6" /> <PackageVersion Include="Dock.Avalonia" Version="11.0.0.7" />
<PackageVersion Include="Dock.Model.ReactiveUI" Version="11.0.0.6" /> <PackageVersion Include="Dock.Model.ReactiveUI" Version="11.0.0.7" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" /> <PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.3" /> <PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.3" />

View file

@ -1,5 +1,3 @@
using InkForge.Api.Data.Infrastructure;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -12,25 +10,15 @@ public class ApiDbcontext(
{ {
public DbSet<WorkspaceEntity> Workspaces { get; set; } = default!; public DbSet<WorkspaceEntity> Workspaces { get; set; } = default!;
public DbSet<WorkspaceVersionEntity> WorkspaceVersions { get; set; } = default!;
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
builder.Entity<WorkspaceEntity>(options => builder.Entity<WorkspaceEntity>(options =>
{ {
options.OwnsOne(m => m.Value);
options.HasKey(m => m.Id); options.HasKey(m => m.Id);
});
builder.Entity<WorkspaceVersionEntity>(options =>
{
options.OwnsOne(m => m.Value); options.OwnsOne(m => m.Value);
options.HasKey(m => m.Version);
options.HasIndex(nameof(WorkspaceVersionEntity.Id), nameof(WorkspaceVersionEntity.Version)).IsUnique();
}); });
} }
} }

View file

@ -1,16 +0,0 @@
using Microsoft.AspNetCore.Identity;
namespace InkForge.Api.Data.Domain;
public class Workspace
{
public string Name { get; set; } = default!;
public DateTimeOffset Created { get; set; }
public IdentityUser Owner { get; set; } = default!;
public DateTimeOffset Updated { get; set; }
public DateTimeOffset? Deleted { get; set; }
}

View file

@ -1,9 +0,0 @@
using InkForge.Api.Data.Domain;
using InkForge.Data;
namespace InkForge.Api.Data.Infrastructure
{
public class WorkspaceEntity : Entity<Workspace, int>;
public class WorkspaceVersionEntity : VersionedEntity<Workspace, int>;
}

View file

@ -0,0 +1,21 @@
using InkForge.Data;
using Microsoft.AspNetCore.Identity;
namespace InkForge.Api.Data
{
public class Workspace
{
public DateTimeOffset Created { get; set; }
public DateTimeOffset? Deleted { get; set; }
public string Name { get; set; } = default!;
public IdentityUser Owner { get; set; } = default!;
public DateTimeOffset Updated { get; set; }
}
public class WorkspaceEntity : Entity<Workspace, int>;
}

View file

@ -8,6 +8,7 @@
<AssemblyName>InkForge</AssemblyName> <AssemblyName>InkForge</AssemblyName>
<RootNamespace>InkForge.Desktop</RootNamespace> <RootNamespace>InkForge.Desktop</RootNamespace>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest> <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
<!-- <TrimMode>partial</TrimMode> -->
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -22,7 +22,7 @@ public class InkForgeFactory : Factory
IsCollapsable = false, IsCollapsable = false,
}; };
_documentDock = new InkForgeDocumentDock _documentDock = new DocumentDock
{ {
Id = "Documents", Id = "Documents",
Title = "Documents", Title = "Documents",

View file

@ -62,26 +62,25 @@ public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject
}; };
var dbFactory = serviceProvider.GetRequiredService<IDbContextFactory<NoteDbContext>>(); var dbFactory = serviceProvider.GetRequiredService<IDbContextFactory<NoteDbContext>>();
await using (var dbContext = dbFactory.CreateDbContext()) await using var dbContext = await dbFactory.CreateDbContextAsync().ConfigureAwait(false);
{
var db = dbContext.Database; var db = dbContext.Database;
await using var transaction = await db.BeginTransactionAsync().ConfigureAwait(false); if ((await db.GetPendingMigrationsAsync().ConfigureAwait(false)).Any())
try
{ {
await db.MigrateAsync().ConfigureAwait(false); if (file.Exists)
}
catch
{ {
// Show Error through TopLevels.ActiveTopLevel file.CopyTo(Path.ChangeExtension(file.FullName, $"{DateTime.Now:s}{file.Extension}"));
await transaction.RollbackAsync().ConfigureAwait(false);
return null;
} }
await transaction.CommitAsync().ConfigureAwait(false); await db.MigrateAsync().ConfigureAwait(false);
} }
scope = null; scope = null;
} }
catch (Exception)
{
// Show Error through TopLevels.ActiveTopLevel
return null;
}
finally finally
{ {
scope?.Dispose(); scope?.Dispose();

View file

@ -34,6 +34,7 @@ public static class InkForgeServiceCollections
// Scoped // Scoped
// - Concrete // - Concrete
services.AddScoped<LocalWorkspaceOptions>(); services.AddScoped<LocalWorkspaceOptions>();
services.AddScoped<NoteStore>();
// - Service // - Service
services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>(); services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>();

View file

@ -1,6 +1,61 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using ReactiveUI;
namespace InkForge.Desktop.Models; namespace InkForge.Desktop.Models;
public class Note public class Note : ReactiveObject
{ {
private readonly ObservableAsPropertyHelper<Note?> _parent;
private readonly BehaviorSubject<int?> _parentId = new(default);
private DateTimeOffset _createdTime;
private int _id;
private string _name = default!;
private DateTimeOffset _updatedTime;
public DateTimeOffset CreatedTime
{
get => _createdTime;
set => this.RaiseAndSetIfChanged(ref _createdTime, value);
}
public int Id
{
get => _id;
set => this.RaiseAndSetIfChanged(ref _id, value);
}
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
public DateTimeOffset UpdatedTime
{
get => _updatedTime;
set => this.RaiseAndSetIfChanged(ref _updatedTime, value);
}
public Note? Parent
{
get => _parent.Value;
set => ParentId = value?.Id;
}
public int? ParentId
{
get => _parentId.Value;
set => _parentId.OnNext(value);
}
public Note(NoteStore noteStore)
{
_parent = _parentId.Select(id => id switch
{
{ } => noteStore.Watch((int)id),
_ => Observable.Empty<Note?>(),
}).Switch(null).ToProperty(this, nameof(Parent), deferSubscription: true);
}
} }

View file

@ -0,0 +1,88 @@
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using DynamicData;
using InkForge.Data;
using Microsoft.EntityFrameworkCore;
namespace InkForge.Desktop.Models;
public class NoteStore
{
private readonly IDbContextFactory<NoteDbContext> _dbContextFactory;
private readonly SourceCache<Note, int> _notesCache = new(m => m.Id);
public ReadOnlyObservableCollection<Note> Notes { get; }
public NoteStore(IDbContextFactory<NoteDbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public void AddNote(Note note)
{
using var dbContext = _dbContextFactory.CreateDbContext();
var entity = ToEntity(note);
var entry = dbContext.Notes.Add(entity);
dbContext.SaveChanges();
}
public Note CreateNote() => new(this);
public Note? GetById(int id)
{
if (((Note?)_notesCache.Lookup(id)) is not Note note)
{
using var dbContext = _dbContextFactory.CreateDbContext();
if (dbContext.Notes.Find(id) is not { } dbNote)
{
return null;
}
note = ToNote(dbNote);
}
return note;
}
public IObservable<Note?> Watch(int id)
{
return _notesCache.WatchValue(id);
}
private NoteEntity ToEntity(Note note)
{
return new()
{
Id = note.Id,
Value = new()
{
Name = note.Name,
Created = note.CreatedTime,
Updated = note.UpdatedTime,
},
Parent = note.Parent switch
{
{ Id: { } parentId } => new()
{
Id = parentId
},
_ => null,
}
};
}
private Note ToNote(NoteEntity entity)
{
var note = CreateNote();
note.Id = entity.Id;
note.Name = entity.Value.Name;
note.CreatedTime = entity.Value.Created;
note.UpdatedTime = entity.Value.Updated;
note.ParentId = entity.Parent?.Id;
return note;
}
}

View file

@ -1,14 +1,11 @@
using InkForge.Data;
using InkForge.Desktop.Data.Options; using InkForge.Desktop.Data.Options;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace InkForge.Desktop.Models; namespace InkForge.Desktop.Models;
public sealed class Workspace : IDisposable public sealed class Workspace : IDisposable
{ {
private readonly IDbContextFactory<NoteDbContext> _dbContextFactory;
private bool _disposedValue; private bool _disposedValue;
private IServiceScope? _scope; private IServiceScope? _scope;
@ -21,20 +18,9 @@ public sealed class Workspace : IDisposable
public Workspace(IServiceScope scope) public Workspace(IServiceScope scope)
{ {
_scope = scope; _scope = scope;
_dbContextFactory = Services.GetRequiredService<IDbContextFactory<NoteDbContext>>();
} }
// public Note AddNote(Note? parent)
// {
// }
public void Dispose() public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{ {
if (!_disposedValue) if (!_disposedValue)
{ {
@ -47,4 +33,14 @@ public sealed class Workspace : IDisposable
_disposedValue = true; _disposedValue = true;
} }
} }
// private async Task LoadNotes()
// {
// await using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
// await foreach (var asdf in dbContext.Notes.AsAsyncEnumerable().ConfigureAwait(false))
// {
// }
// _ = (await dbContext.Notes.ToListAsync().ConfigureAwait(false));
// }
} }

View file

@ -0,0 +1,147 @@
using System.Reactive.Disposables;
namespace System.Reactive.Linq;
public static class ObservableSwitch
{
public static IObservable<T> Switch<T>(this IObservable<IObservable<T>> observable, T defaultValue)
{
return new SwitchObservable<T>(observable, defaultValue);
}
private class SwitchObservable<T>(IObservable<IObservable<T>> sources, T defaultValue) : ObservableBase<T>
{
protected override IDisposable SubscribeCore(IObserver<T> observer)
{
_ _ = new(defaultValue, observer);
_.Run(sources);
return _;
}
private class _(T defaultValue, IObserver<T> observer) : ObserverBase<IObservable<T>>
{
private readonly SerialDisposable _innerSerialDisposable = new();
private readonly SingleAssignmentDisposable _upstream = new();
private bool _hasLatest;
private int _latest;
private bool _stopped = false;
public void Run(IObservable<IObservable<T>> sources)
{
_upstream.Disposable = sources.Subscribe(this);
if (_innerSerialDisposable.Disposable is null)
{
observer.OnNext(defaultValue);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_innerSerialDisposable?.Dispose();
}
base.Dispose(disposing);
}
protected void ForwardOnCompleted() => observer.OnCompleted();
protected void ForwardOnError(Exception error) => observer.OnError(error);
protected void ForwardOnNext(T value) => observer.OnNext(value);
protected override void OnCompletedCore()
{
_upstream.Dispose();
_stopped = true;
if (!_hasLatest)
{
observer.OnCompleted();
}
}
protected override void OnErrorCore(Exception error) => ForwardOnError(error);
protected override void OnNextCore(IObservable<T> value)
{
uint id = unchecked((uint)Interlocked.Increment(ref _latest));
_hasLatest = true;
var innerObserver = new InnerObserver(this, id, defaultValue);
_innerSerialDisposable.Disposable = innerObserver;
innerObserver.Subscribe(value);
}
private class InnerObserver(_ parent, uint id, T defaultValue) : ObserverBase<T>
{
private readonly SingleAssignmentDisposable _upstream = new();
public bool Found { get; set; } = false;
public void Subscribe(IObservable<T> upstream)
{
_upstream.Disposable = upstream.SubscribeSafe(this);
if (!Found)
{
OnNext(defaultValue);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_upstream.Dispose();
}
base.Dispose(disposing);
}
protected override void OnCompletedCore()
{
Dispose();
if (parent._latest == id)
{
parent._hasLatest = false;
if (!Found)
{
OnNextCore(defaultValue);
}
if (parent._stopped)
{
parent.ForwardOnCompleted();
}
}
}
protected override void OnErrorCore(Exception error)
{
Dispose();
if (parent._latest == id)
{
if (!Found)
{
OnNextCore(defaultValue);
}
parent.ForwardOnError(error);
}
}
protected override void OnNextCore(T value)
{
Found = true;
if (parent._latest == id)
{
parent.ForwardOnNext(value);
}
}
}
}
}
}

View file

@ -4,15 +4,20 @@ using Microsoft.Extensions.DependencyInjection;
namespace InkForge.Desktop.ViewModels.Workspaces namespace InkForge.Desktop.ViewModels.Workspaces
{ {
public class WorkspaceViewModel(Workspace workspace) public class WorkspaceViewModel
{ {
// private readonly Workspace _workspace; private readonly NoteStore _noteStore;
// private readonly ObservableAsPropertyHelper<string> _workspaceNameProperty; // private readonly ObservableAsPropertyHelper<string> _workspaceNameProperty;
// public string WorkspaceName => _workspaceNameProperty.Value; // public string WorkspaceName => _workspaceNameProperty.Value;
// public ReactiveCommand<Unit, Unit> AddDocument { get; } // public ReactiveCommand<Unit, Unit> AddDocument { get; }
public WorkspaceViewModel(NoteStore noteStore)
{
_noteStore = noteStore;
}
// public WorkspacesViewModel(Workspace workspace) // public WorkspacesViewModel(Workspace workspace)
// { // {
// _workspace = workspace; // _workspace = workspace;

View file

@ -20,7 +20,7 @@
Spacing="3" Spacing="3"
Grid.Column="1" Grid.Column="1"
Grid.Row="0"> Grid.Row="0">
<Button> <Button AutomationProperties.Name="Add">
<inkforge:FluentSymbolIcon Symbol="document_add" /> <inkforge:FluentSymbolIcon Symbol="document_add" />
</Button> </Button>
<Button> <Button>

View file

@ -0,0 +1,3 @@
namespace InkForge.Data;
public class Blob : Entity<byte[], string>;

View file

@ -1,18 +0,0 @@
using InkForge.Data.Infrastructure;
namespace InkForge.Data.Domain;
public class Note
{
public DateTimeOffset Created { get; set; }
public NoteEntity? Parent { get; set; }
public string Name { get; set; } = default!;
public DateTimeOffset Updated { get; set; }
public DateTimeOffset? Deleted { get; set; }
public Blob Content { get; set; } = default!;
}

View file

@ -1,5 +1,3 @@
using System.Numerics;
namespace InkForge.Data namespace InkForge.Data
{ {
public abstract class ValueEntity<TEntity> public abstract class ValueEntity<TEntity>
@ -10,14 +8,13 @@ namespace InkForge.Data
public abstract class Entity<TEntity, TKey> public abstract class Entity<TEntity, TKey>
: ValueEntity<TEntity> : ValueEntity<TEntity>
{ {
public TKey? Id { get; set; } public TKey Id { get; set; } = default!;
} }
public abstract class VersionedEntity<TEntity, TKey> public abstract class Entity<TSelf, TEntity, TKey>
: ValueEntity<TEntity> : Entity<TEntity, TKey>
where TSelf : Entity<TSelf, TEntity, TKey>
{ {
public TKey Id { get; set; } public TSelf? Parent { get; set; }
public int? Version { get; set; }
} }
} }

View file

@ -1,8 +0,0 @@
namespace InkForge.Data;
public class Blob
{
public string Id { get; set; } = default!;
public byte[] Content { get; set; } = default!;
}

View file

@ -1,5 +0,0 @@
namespace InkForge.Data.Infrastructure;
public class MetadataEntity : Entity<string, string>;
public class MetadataVersionEntity : VersionedEntity<string, string>;

View file

@ -1,8 +0,0 @@
using InkForge.Data.Domain;
namespace InkForge.Data.Infrastructure
{
public class NoteEntity : Entity<Note, int>;
public class NoteVersionEntity : VersionedEntity<Note, int>;
}

View file

@ -0,0 +1,3 @@
namespace InkForge.Data;
public class MetadataEntity : Entity<string, string>;

View file

@ -1,22 +1,19 @@
using InkForge.Data.Infrastructure;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace InkForge.Data; namespace InkForge.Data;
public class NoteDbContext( public class NoteDbContext(
DbContextOptions<NoteDbContext> options DbContextOptions options
) : DbContext(options) ) : DbContext(options)
{ {
public DbSet<Blob> Blobs { get; set; } = default!; public DbSet<Blob> Blobs { get; set; } = default!;
public DbSet<MetadataEntity> Metadata { get; set; } = default!; public DbSet<MetadataEntity> Metadata { get; set; } = default!;
public DbSet<MetadataVersionEntity> MetadataHistory { get; set; } = default!;
public DbSet<NoteEntity> Notes { get; set; } = default!; public DbSet<NoteEntity> Notes { get; set; } = default!;
public DbSet<NoteVersionEntity> NoteVersions { get; set; } = default!; public NoteDbContext(DbContextOptions<NoteDbContext> options) : this((DbContextOptions)options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
@ -25,26 +22,13 @@ public class NoteDbContext(
options.HasKey(m => m.Id); options.HasKey(m => m.Id);
}); });
modelBuilder.Entity<MetadataVersionEntity>(options =>
{
options.Property(m => m.Id).IsRequired();
options.HasKey(m => m.Version);
options.HasIndex(nameof(MetadataVersionEntity.Id), nameof(MetadataVersionEntity.Version)).IsUnique();
});
modelBuilder.Entity<NoteEntity>(options => modelBuilder.Entity<NoteEntity>(options =>
{ {
options.OwnsOne(m => m.Value);
options.HasKey(m => m.Id); options.HasKey(m => m.Id);
});
modelBuilder.Entity<NoteVersionEntity>(options =>
{
options.OwnsOne(m => m.Value); options.OwnsOne(m => m.Value);
options.Property(m => m.Id).IsRequired();
options.HasKey(m => m.Version); options.HasOne(m => m.Parent);
options.HasIndex(nameof(NoteVersionEntity.Id), nameof(NoteVersionEntity.Version)).IsUnique();
}); });
} }
} }

View file

@ -0,0 +1,17 @@
namespace InkForge.Data
{
public class Note
{
public DateTimeOffset Created { get; set; }
public string Name { get; set; } = default!;
public DateTimeOffset Updated { get; set; }
public DateTimeOffset? Deleted { get; set; }
public Blob Content { get; set; } = default!;
}
public class NoteEntity : Entity<NoteEntity, Note, int>;
}

View file

@ -0,0 +1,121 @@
// <auto-generated />
using System;
using InkForge.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace InkForge.Sqlite.Migrations
{
[DbContext(typeof(NoteDbContext))]
[Migration("20240405000000__03_DropHistory")]
partial class _03_DropHistory
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.3");
modelBuilder.Entity("InkForge.Data.Blob", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<byte[]>("Value")
.IsRequired()
.HasColumnType("BLOB");
b.HasKey("Id");
b.ToTable("Blobs");
});
modelBuilder.Entity("InkForge.Data.MetadataEntity", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Metadata");
});
modelBuilder.Entity("InkForge.Data.NoteEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("Notes");
});
modelBuilder.Entity("InkForge.Data.NoteEntity", b =>
{
b.HasOne("InkForge.Data.NoteEntity", "Parent")
.WithMany()
.HasForeignKey("ParentId");
b.OwnsOne("InkForge.Data.Note", "Value", b1 =>
{
b1.Property<int>("NoteEntityId")
.HasColumnType("INTEGER");
b1.Property<string>("ContentId")
.IsRequired()
.HasColumnType("TEXT");
b1.Property<DateTimeOffset>("Created")
.HasColumnType("TEXT");
b1.Property<DateTimeOffset?>("Deleted")
.HasColumnType("TEXT");
b1.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b1.Property<DateTimeOffset>("Updated")
.HasColumnType("TEXT");
b1.HasKey("NoteEntityId");
b1.HasIndex("ContentId");
b1.ToTable("Notes");
b1.HasOne("InkForge.Data.Blob", "Content")
.WithMany()
.HasForeignKey("ContentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b1.WithOwner()
.HasForeignKey("NoteEntityId");
b1.Navigation("Content");
});
b.Navigation("Parent");
b.Navigation("Value")
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,149 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace InkForge.Sqlite.Migrations
{
/// <inheritdoc />
public partial class _03_DropHistory : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MetadataHistory");
migrationBuilder.DropTable(
name: "NoteVersions");
migrationBuilder.RenameColumn(
name: "Content",
table: "Blobs",
newName: "Value");
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "Notes",
type: "INTEGER",
nullable: false,
oldClrType: typeof(int),
oldType: "INTEGER")
.OldAnnotation("Sqlite:Autoincrement", true);
migrationBuilder.AddColumn<int>(
name: "ParentId",
table: "Notes",
type: "INTEGER",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Notes_ParentId",
table: "Notes",
column: "ParentId");
migrationBuilder.AddForeignKey(
name: "FK_Notes_Notes_ParentId",
table: "Notes",
column: "ParentId",
principalTable: "Notes",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Notes_Notes_ParentId",
table: "Notes");
migrationBuilder.DropIndex(
name: "IX_Notes_ParentId",
table: "Notes");
migrationBuilder.DropColumn(
name: "ParentId",
table: "Notes");
migrationBuilder.RenameColumn(
name: "Value",
table: "Blobs",
newName: "Content");
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "Notes",
type: "INTEGER",
nullable: false,
oldClrType: typeof(int),
oldType: "INTEGER")
.Annotation("Sqlite:Autoincrement", true);
migrationBuilder.CreateTable(
name: "MetadataHistory",
columns: table => new
{
Version = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Id = table.Column<string>(type: "TEXT", nullable: true),
Value = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MetadataHistory", x => x.Version);
});
migrationBuilder.CreateTable(
name: "NoteVersions",
columns: table => new
{
Version = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Id = table.Column<int>(type: "INTEGER", nullable: false),
Value_ContentId = table.Column<string>(type: "TEXT", nullable: false),
Value_ParentId = table.Column<int>(type: "INTEGER", nullable: true),
Value_Created = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
Value_Deleted = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
Value_Name = table.Column<string>(type: "TEXT", nullable: false),
Value_Updated = table.Column<DateTimeOffset>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_NoteVersions", x => x.Version);
table.ForeignKey(
name: "FK_NoteVersions_Blobs_Value_ContentId",
column: x => x.Value_ContentId,
principalTable: "Blobs",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_NoteVersions_Notes_Value_ParentId",
column: x => x.Value_ParentId,
principalTable: "Notes",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_MetadataHistory_Id_Version",
table: "MetadataHistory",
columns: new[] { "Id", "Version" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_NoteVersions_Id_Version",
table: "NoteVersions",
columns: new[] { "Id", "Version" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_NoteVersions_Value_ContentId",
table: "NoteVersions",
column: "Value_ContentId");
migrationBuilder.CreateIndex(
name: "IX_NoteVersions_Value_ParentId",
table: "NoteVersions",
column: "Value_ParentId");
}
}
}

View file

@ -15,23 +15,23 @@ namespace InkForge.Sqlite.Migrations
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.2"); modelBuilder.HasAnnotation("ProductVersion", "8.0.3");
modelBuilder.Entity("InkForge.Data.Blob", b => modelBuilder.Entity("InkForge.Data.Blob", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<byte[]>("Content") b.Property<byte[]>("Value")
.IsRequired() .IsRequired()
.HasColumnType("BLOB"); .HasColumnType("BLOB");
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Blobs"); b.ToTable("Blobs", (string)null);
}); });
modelBuilder.Entity("InkForge.Data.Infrastructure.MetadataEntity", b => modelBuilder.Entity("InkForge.Data.MetadataEntity", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -42,111 +42,34 @@ namespace InkForge.Sqlite.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Metadata"); b.ToTable("Metadata", (string)null);
}); });
modelBuilder.Entity("InkForge.Data.Infrastructure.MetadataVersionEntity", b => modelBuilder.Entity("InkForge.Data.NoteEntity", b =>
{
b.Property<int?>("Version")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Version");
b.HasIndex("Id", "Version")
.IsUnique();
b.ToTable("MetadataHistory");
});
modelBuilder.Entity("InkForge.Data.Infrastructure.NoteEntity", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Notes"); b.HasIndex("ParentId");
b.ToTable("Notes", (string)null);
}); });
modelBuilder.Entity("InkForge.Data.Infrastructure.NoteVersionEntity", b => modelBuilder.Entity("InkForge.Data.NoteEntity", b =>
{ {
b.Property<int?>("Version") b.HasOne("InkForge.Data.NoteEntity", "Parent")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Id")
.HasColumnType("INTEGER");
b.HasKey("Version");
b.HasIndex("Id", "Version")
.IsUnique();
b.ToTable("NoteVersions");
});
modelBuilder.Entity("InkForge.Data.Infrastructure.NoteEntity", b =>
{
b.OwnsOne("InkForge.Data.Domain.Note", "Value", b1 =>
{
b1.Property<int>("ParentId")
.HasColumnType("INTEGER");
b1.Property<string>("ContentId")
.IsRequired()
.HasColumnType("TEXT");
b1.Property<DateTimeOffset>("Created")
.HasColumnType("TEXT");
b1.Property<DateTimeOffset?>("Deleted")
.HasColumnType("TEXT");
b1.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b1.Property<DateTimeOffset>("Updated")
.HasColumnType("TEXT");
b1.HasKey("ParentId");
b1.HasIndex("ContentId");
b1.ToTable("Notes");
b1.HasOne("InkForge.Data.Blob", "Content")
.WithMany() .WithMany()
.HasForeignKey("ContentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b1.WithOwner("Parent")
.HasForeignKey("ParentId"); .HasForeignKey("ParentId");
b1.Navigation("Content"); b.OwnsOne("InkForge.Data.NoteEntity.Value#InkForge.Data.Note", "Value", b1 =>
b1.Navigation("Parent");
});
b.Navigation("Value")
.IsRequired();
});
modelBuilder.Entity("InkForge.Data.Infrastructure.NoteVersionEntity", b =>
{ {
b.OwnsOne("InkForge.Data.Domain.Note", "Value", b1 => b1.Property<int>("NoteEntityId")
{
b1.Property<int>("NoteVersionEntityVersion")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b1.Property<string>("ContentId") b1.Property<string>("ContentId")
@ -163,19 +86,14 @@ namespace InkForge.Sqlite.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b1.Property<int?>("ParentId")
.HasColumnType("INTEGER");
b1.Property<DateTimeOffset>("Updated") b1.Property<DateTimeOffset>("Updated")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b1.HasKey("NoteVersionEntityVersion"); b1.HasKey("NoteEntityId");
b1.HasIndex("ContentId"); b1.HasIndex("ContentId");
b1.HasIndex("ParentId"); b1.ToTable("Notes", (string)null);
b1.ToTable("NoteVersions");
b1.HasOne("InkForge.Data.Blob", "Content") b1.HasOne("InkForge.Data.Blob", "Content")
.WithMany() .WithMany()
@ -184,17 +102,13 @@ namespace InkForge.Sqlite.Migrations
.IsRequired(); .IsRequired();
b1.WithOwner() b1.WithOwner()
.HasForeignKey("NoteVersionEntityVersion"); .HasForeignKey("NoteEntityId");
b1.HasOne("InkForge.Data.Infrastructure.NoteEntity", "Parent")
.WithMany()
.HasForeignKey("ParentId");
b1.Navigation("Content"); b1.Navigation("Content");
b1.Navigation("Parent");
}); });
b.Navigation("Parent");
b.Navigation("Value") b.Navigation("Value")
.IsRequired(); .IsRequired();
}); });