Notes
This commit is contained in:
parent
4c2b5cca93
commit
43b4d50e43
28 changed files with 674 additions and 249 deletions
|
|
@ -3,7 +3,7 @@
|
|||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "8.0.2",
|
||||
"version": "8.0.3",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.0.10" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" 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.Model.ReactiveUI" Version="11.0.0.6" />
|
||||
<PackageVersion Include="Dock.Avalonia" Version="11.0.0.7" />
|
||||
<PackageVersion Include="Dock.Model.ReactiveUI" Version="11.0.0.7" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.3" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
using InkForge.Api.Data.Infrastructure;
|
||||
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
@ -12,25 +10,15 @@ public class ApiDbcontext(
|
|||
{
|
||||
public DbSet<WorkspaceEntity> Workspaces { get; set; } = default!;
|
||||
|
||||
public DbSet<WorkspaceVersionEntity> WorkspaceVersions { get; set; } = default!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
builder.Entity<WorkspaceEntity>(options =>
|
||||
{
|
||||
options.OwnsOne(m => m.Value);
|
||||
|
||||
options.HasKey(m => m.Id);
|
||||
});
|
||||
|
||||
builder.Entity<WorkspaceVersionEntity>(options =>
|
||||
{
|
||||
options.OwnsOne(m => m.Value);
|
||||
|
||||
options.HasKey(m => m.Version);
|
||||
options.HasIndex(nameof(WorkspaceVersionEntity.Id), nameof(WorkspaceVersionEntity.Version)).IsUnique();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -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>;
|
||||
}
|
||||
21
InkForge.Api.Data/Workspaces.cs
Normal file
21
InkForge.Api.Data/Workspaces.cs
Normal 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>;
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
<AssemblyName>InkForge</AssemblyName>
|
||||
<RootNamespace>InkForge.Desktop</RootNamespace>
|
||||
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
|
||||
<!-- <TrimMode>partial</TrimMode> -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class InkForgeFactory : Factory
|
|||
IsCollapsable = false,
|
||||
};
|
||||
|
||||
_documentDock = new InkForgeDocumentDock
|
||||
_documentDock = new DocumentDock
|
||||
{
|
||||
Id = "Documents",
|
||||
Title = "Documents",
|
||||
|
|
|
|||
|
|
@ -62,26 +62,25 @@ public class WorkspaceManager(IServiceProvider serviceProvider) : ReactiveObject
|
|||
};
|
||||
|
||||
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;
|
||||
if ((await db.GetPendingMigrationsAsync().ConfigureAwait(false)).Any())
|
||||
{
|
||||
var db = dbContext.Database;
|
||||
await using var transaction = await db.BeginTransactionAsync().ConfigureAwait(false);
|
||||
try
|
||||
if (file.Exists)
|
||||
{
|
||||
await db.MigrateAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Show Error through TopLevels.ActiveTopLevel
|
||||
await transaction.RollbackAsync().ConfigureAwait(false);
|
||||
return null;
|
||||
file.CopyTo(Path.ChangeExtension(file.FullName, $"{DateTime.Now:s}{file.Extension}"));
|
||||
}
|
||||
|
||||
await transaction.CommitAsync().ConfigureAwait(false);
|
||||
await db.MigrateAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
scope = null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Show Error through TopLevels.ActiveTopLevel
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope?.Dispose();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public static class InkForgeServiceCollections
|
|||
// Scoped
|
||||
// - Concrete
|
||||
services.AddScoped<LocalWorkspaceOptions>();
|
||||
services.AddScoped<NoteStore>();
|
||||
|
||||
// - Service
|
||||
services.AddScoped<IDbContextFactory<NoteDbContext>, NoteDbContextFactory>();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,61 @@
|
|||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
using ReactiveUI;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
88
app/InkForge.Desktop/Models/NoteStore.cs
Normal file
88
app/InkForge.Desktop/Models/NoteStore.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,11 @@
|
|||
using InkForge.Data;
|
||||
using InkForge.Desktop.Data.Options;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace InkForge.Desktop.Models;
|
||||
|
||||
public sealed class Workspace : IDisposable
|
||||
{
|
||||
private readonly IDbContextFactory<NoteDbContext> _dbContextFactory;
|
||||
private bool _disposedValue;
|
||||
private IServiceScope? _scope;
|
||||
|
||||
|
|
@ -21,20 +18,9 @@ public sealed class Workspace : IDisposable
|
|||
public Workspace(IServiceScope scope)
|
||||
{
|
||||
_scope = scope;
|
||||
_dbContextFactory = Services.GetRequiredService<IDbContextFactory<NoteDbContext>>();
|
||||
}
|
||||
|
||||
// public Note AddNote(Note? parent)
|
||||
// {
|
||||
// }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
|
|
@ -47,4 +33,14 @@ public sealed class Workspace : IDisposable
|
|||
_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));
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
147
app/InkForge.Desktop/System/Reactive/Linq/Observable.Switch.cs
Normal file
147
app/InkForge.Desktop/System/Reactive/Linq/Observable.Switch.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,15 +4,20 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
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;
|
||||
|
||||
// public string WorkspaceName => _workspaceNameProperty.Value;
|
||||
|
||||
// public ReactiveCommand<Unit, Unit> AddDocument { get; }
|
||||
|
||||
public WorkspaceViewModel(NoteStore noteStore)
|
||||
{
|
||||
_noteStore = noteStore;
|
||||
}
|
||||
|
||||
// public WorkspacesViewModel(Workspace workspace)
|
||||
// {
|
||||
// _workspace = workspace;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
Spacing="3"
|
||||
Grid.Column="1"
|
||||
Grid.Row="0">
|
||||
<Button>
|
||||
<Button AutomationProperties.Name="Add">
|
||||
<inkforge:FluentSymbolIcon Symbol="document_add" />
|
||||
</Button>
|
||||
<Button>
|
||||
|
|
|
|||
3
shared/InkForge.Data/Blob.cs
Normal file
3
shared/InkForge.Data/Blob.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace InkForge.Data;
|
||||
|
||||
public class Blob : Entity<byte[], string>;
|
||||
|
|
@ -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!;
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace InkForge.Data
|
||||
{
|
||||
public abstract class ValueEntity<TEntity>
|
||||
|
|
@ -10,14 +8,13 @@ namespace InkForge.Data
|
|||
public abstract class Entity<TEntity, TKey>
|
||||
: ValueEntity<TEntity>
|
||||
{
|
||||
public TKey? Id { get; set; }
|
||||
public TKey Id { get; set; } = default!;
|
||||
}
|
||||
|
||||
public abstract class VersionedEntity<TEntity, TKey>
|
||||
: ValueEntity<TEntity>
|
||||
public abstract class Entity<TSelf, TEntity, TKey>
|
||||
: Entity<TEntity, TKey>
|
||||
where TSelf : Entity<TSelf, TEntity, TKey>
|
||||
{
|
||||
public TKey Id { get; set; }
|
||||
|
||||
public int? Version { get; set; }
|
||||
public TSelf? Parent { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
namespace InkForge.Data;
|
||||
|
||||
public class Blob
|
||||
{
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
public byte[] Content { get; set; } = default!;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
namespace InkForge.Data.Infrastructure;
|
||||
|
||||
public class MetadataEntity : Entity<string, string>;
|
||||
|
||||
public class MetadataVersionEntity : VersionedEntity<string, string>;
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
using InkForge.Data.Domain;
|
||||
|
||||
namespace InkForge.Data.Infrastructure
|
||||
{
|
||||
public class NoteEntity : Entity<Note, int>;
|
||||
|
||||
public class NoteVersionEntity : VersionedEntity<Note, int>;
|
||||
}
|
||||
3
shared/InkForge.Data/Metadata.cs
Normal file
3
shared/InkForge.Data/Metadata.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace InkForge.Data;
|
||||
|
||||
public class MetadataEntity : Entity<string, string>;
|
||||
|
|
@ -1,22 +1,19 @@
|
|||
using InkForge.Data.Infrastructure;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace InkForge.Data;
|
||||
|
||||
public class NoteDbContext(
|
||||
DbContextOptions<NoteDbContext> options
|
||||
DbContextOptions options
|
||||
) : DbContext(options)
|
||||
{
|
||||
public DbSet<Blob> Blobs { 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<NoteVersionEntity> NoteVersions { get; set; } = default!;
|
||||
public NoteDbContext(DbContextOptions<NoteDbContext> options) : this((DbContextOptions)options)
|
||||
{ }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
|
|
@ -25,26 +22,13 @@ public class NoteDbContext(
|
|||
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 =>
|
||||
{
|
||||
options.OwnsOne(m => m.Value);
|
||||
|
||||
options.HasKey(m => m.Id);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<NoteVersionEntity>(options =>
|
||||
{
|
||||
options.OwnsOne(m => m.Value);
|
||||
options.Property(m => m.Id).IsRequired();
|
||||
options.HasKey(m => m.Version);
|
||||
options.HasIndex(nameof(NoteVersionEntity.Id), nameof(NoteVersionEntity.Version)).IsUnique();
|
||||
|
||||
options.HasOne(m => m.Parent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
shared/InkForge.Data/Notes.cs
Normal file
17
shared/InkForge.Data/Notes.cs
Normal 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>;
|
||||
}
|
||||
121
shared/migrations/InkForge.Sqlite/Migrations/20240405000000__03_DropHistory.Designer.cs
generated
Normal file
121
shared/migrations/InkForge.Sqlite/Migrations/20240405000000__03_DropHistory.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,23 +15,23 @@ namespace InkForge.Sqlite.Migrations
|
|||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.3");
|
||||
|
||||
modelBuilder.Entity("InkForge.Data.Blob", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("Content")
|
||||
b.Property<byte[]>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
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")
|
||||
.HasColumnType("TEXT");
|
||||
|
|
@ -42,63 +42,34 @@ namespace InkForge.Sqlite.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Metadata");
|
||||
b.ToTable("Metadata", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("InkForge.Data.Infrastructure.MetadataVersionEntity", 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 =>
|
||||
modelBuilder.Entity("InkForge.Data.NoteEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ParentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
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")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
b.HasOne("InkForge.Data.NoteEntity", "Parent")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentId");
|
||||
|
||||
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 =>
|
||||
b.OwnsOne("InkForge.Data.NoteEntity.Value#InkForge.Data.Note", "Value", b1 =>
|
||||
{
|
||||
b1.Property<int>("ParentId")
|
||||
b1.Property<int>("NoteEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("ContentId")
|
||||
|
|
@ -118,64 +89,11 @@ namespace InkForge.Sqlite.Migrations
|
|||
b1.Property<DateTimeOffset>("Updated")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.HasKey("ParentId");
|
||||
b1.HasKey("NoteEntityId");
|
||||
|
||||
b1.HasIndex("ContentId");
|
||||
|
||||
b1.ToTable("Notes");
|
||||
|
||||
b1.HasOne("InkForge.Data.Blob", "Content")
|
||||
.WithMany()
|
||||
.HasForeignKey("ContentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b1.WithOwner("Parent")
|
||||
.HasForeignKey("ParentId");
|
||||
|
||||
b1.Navigation("Content");
|
||||
|
||||
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>("NoteVersionEntityVersion")
|
||||
.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<int?>("ParentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<DateTimeOffset>("Updated")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.HasKey("NoteVersionEntityVersion");
|
||||
|
||||
b1.HasIndex("ContentId");
|
||||
|
||||
b1.HasIndex("ParentId");
|
||||
|
||||
b1.ToTable("NoteVersions");
|
||||
b1.ToTable("Notes", (string)null);
|
||||
|
||||
b1.HasOne("InkForge.Data.Blob", "Content")
|
||||
.WithMany()
|
||||
|
|
@ -184,17 +102,13 @@ namespace InkForge.Sqlite.Migrations
|
|||
.IsRequired();
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("NoteVersionEntityVersion");
|
||||
|
||||
b1.HasOne("InkForge.Data.Infrastructure.NoteEntity", "Parent")
|
||||
.WithMany()
|
||||
.HasForeignKey("ParentId");
|
||||
.HasForeignKey("NoteEntityId");
|
||||
|
||||
b1.Navigation("Content");
|
||||
|
||||
b1.Navigation("Parent");
|
||||
});
|
||||
|
||||
b.Navigation("Parent");
|
||||
|
||||
b.Navigation("Value")
|
||||
.IsRequired();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue