WIP on EF Core data context

This commit is contained in:
Daniel J. Summers 2022-02-24 22:52:01 -05:00
parent 69bc105e40
commit 8bc2cf0aa5
13 changed files with 613 additions and 0 deletions

View File

@ -0,0 +1,42 @@
namespace MyWebLog.Data;
/// <summary>
/// A category under which a post may be identfied
/// </summary>
public class Category
{
/// <summary>
/// The ID of the category
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// The displayed name
/// </summary>
public string Name { get; set; } = "";
/// <summary>
/// The slug (used in category URLs)
/// </summary>
public string Slug { get; set; } = "";
/// <summary>
/// A longer description of the category
/// </summary>
public string? Description { get; set; } = null;
/// <summary>
/// The parent ID of this category (if a subcategory)
/// </summary>
public string? ParentId { get; set; } = null;
/// <summary>
/// The parent of this category (if a subcategory)
/// </summary>
public Category? Parent { get; set; } = default;
/// <summary>
/// The posts assigned to this category
/// </summary>
public ICollection<Post> Posts { get; set; } = default!;
}

View File

@ -0,0 +1,62 @@
namespace MyWebLog.Data;
/// <summary>
/// A comment on a post
/// </summary>
public class Comment
{
/// <summary>
/// The ID of the comment
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// The ID of the post to which this comment applies
/// </summary>
public string PostId { get; set; } = "";
/// <summary>
/// The post to which this comment applies
/// </summary>
public Post Post { get; set; } = default!;
/// <summary>
/// The ID of the comment to which this comment is a reply
/// </summary>
public string? InReplyToId { get; set; } = null;
/// <summary>
/// The comment to which this comment is a reply
/// </summary>
public Comment? InReplyTo { get; set; } = default;
/// <summary>
/// The name of the commentor
/// </summary>
public string Name { get; set; } = "";
/// <summary>
/// The e-mail address of the commentor
/// </summary>
public string Email { get; set; } = "";
/// <summary>
/// The URL of the commentor's personal website
/// </summary>
public string? Url { get; set; } = null;
/// <summary>
/// The status of the comment
/// </summary>
public CommentStatus Status { get; set; } = CommentStatus.Pending;
/// <summary>
/// When the comment was posted
/// </summary>
public DateTime PostedOn { get; set; } = DateTime.UtcNow;
/// <summary>
/// The text of the comment
/// </summary>
public string Text { get; set; } = "";
}

View File

@ -0,0 +1,47 @@
namespace MyWebLog.Data;
/// <summary>
/// The source format for a revision
/// </summary>
public enum RevisionSource
{
/// <summary>Markdown text</summary>
Markdown,
/// <summary>HTML</summary>
Html
}
/// <summary>
/// A level of authorization for a given web log
/// </summary>
public enum AuthorizationLevel
{
/// <summary>The user may administer all aspects of a web log</summary>
Administrator,
/// <summary>The user is a known user of a web log</summary>
User
}
/// <summary>
/// Statuses for posts
/// </summary>
public enum PostStatus
{
/// <summary>The post should not be publicly available</summary>
Draft,
/// <summary>The post is publicly viewable</summary>
Published
}
/// <summary>
/// Statuses for post comments
/// </summary>
public enum CommentStatus
{
/// <summary>The comment is approved</summary>
Approved,
/// <summary>The comment has yet to be approved</summary>
Pending,
/// <summary>The comment was unsolicited and unwelcome</summary>
Spam
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

62
src/MyWebLog.Data/Page.cs Normal file
View File

@ -0,0 +1,62 @@
namespace MyWebLog.Data;
/// <summary>
/// A page (text not associated with a date/time)
/// </summary>
public class Page
{
/// <summary>
/// The ID of this page
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// The ID of the author of this page
/// </summary>
public string AuthorId { get; set; } = "";
/// <summary>
/// The author of this page
/// </summary>
public WebLogUser Author { get; set; } = default!;
/// <summary>
/// The title of the page
/// </summary>
public string Title { get; set; } = "";
/// <summary>
/// The link at which this page is displayed
/// </summary>
public string Permalink { get; set; } = "";
/// <summary>
/// The instant this page was published
/// </summary>
public DateTime PublishedOn { get; set; } = DateTime.MinValue;
/// <summary>
/// The instant this page was last updated
/// </summary>
public DateTime UpdatedOn { get; set; } = DateTime.MinValue;
/// <summary>
/// Whether this page shows as part of the web log's navigation
/// </summary>
public bool ShowInPageList { get; set; } = false;
/// <summary>
/// The current text of the page
/// </summary>
public string Text { get; set; } = "";
/// <summary>
/// Permalinks at which this page may have been previously served (useful for migrated content)
/// </summary>
public ICollection<PagePermalink> PriorPermalinks { get; set; } = default!;
/// <summary>
/// Revisions of this page
/// </summary>
public ICollection<PageRevision> Revisions { get; set; } = default!;
}

View File

@ -0,0 +1,49 @@
namespace MyWebLog.Data;
/// <summary>
/// A permalink which a post or page used to have
/// </summary>
public abstract class Permalink
{
/// <summary>
/// The ID of this permalink
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// The link
/// </summary>
public string Url { get; set; } = "";
}
/// <summary>
/// A prior permalink for a page
/// </summary>
public class PagePermalink : Permalink
{
/// <summary>
/// The ID of the page to which this permalink belongs
/// </summary>
public string PageId { get; set; } = "";
/// <summary>
/// The page to which this permalink belongs
/// </summary>
public Page Page { get; set; } = default!;
}
/// <summary>
/// A prior permalink for a post
/// </summary>
public class PostPermalink : Permalink
{
/// <summary>
/// The ID of the post to which this permalink belongs
/// </summary>
public string PostId { get; set; } = "";
/// <summary>
/// The post to which this permalink belongs
/// </summary>
public Post Post { get; set; } = default!;
}

72
src/MyWebLog.Data/Post.cs Normal file
View File

@ -0,0 +1,72 @@
namespace MyWebLog.Data;
/// <summary>
/// A web log post
/// </summary>
public class Post
{
/// <summary>
/// The ID of this post
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// The ID of the author of this post
/// </summary>
public string AuthorId { get; set; } = "";
/// <summary>
/// The author of the post
/// </summary>
public WebLogUser Author { get; set; } = default!;
/// <summary>
/// The status
/// </summary>
public PostStatus Status { get; set; } = PostStatus.Draft;
/// <summary>
/// The title
/// </summary>
public string Title { get; set; } = "";
/// <summary>
/// The link at which the post resides
/// </summary>
public string Permalink { get; set; } = "";
/// <summary>
/// The instant on which the post was originally published
/// </summary>
public DateTime? PublishedOn { get; set; } = null;
/// <summary>
/// The instant on which the post was last updated
/// </summary>
public DateTime UpdatedOn { get; set; } = DateTime.MinValue;
/// <summary>
/// The text of the post in HTML (ready to display) format
/// </summary>
public string Text { get; set; } = "";
/// <summary>
/// The Ids of the categories to which this is assigned
/// </summary>
public ICollection<Category> Categories { get; set; } = default!;
/// <summary>
/// The tags for the post
/// </summary>
public ICollection<Tag> Tags { get; set; } = default!;
/// <summary>
/// Permalinks at which this post may have been previously served (useful for migrated content)
/// </summary>
public ICollection<PostPermalink> PriorPermalinks { get; set; } = default!;
/// <summary>
/// The revisions for this post
/// </summary>
public ICollection<PostRevision> Revisions { get; set; } = default!;
}

View File

@ -0,0 +1,59 @@
namespace MyWebLog.Data;
/// <summary>
/// A revision of a page or post
/// </summary>
public abstract class Revision
{
/// <summary>
/// The ID of this revision
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// When this revision was saved
/// </summary>
public DateTime AsOf { get; set; } = DateTime.UtcNow;
/// <summary>
/// The source language (Markdown or HTML)
/// </summary>
public RevisionSource SourceType { get; set; } = RevisionSource.Html;
/// <summary>
/// The text of the revision
/// </summary>
public string Text { get; set; } = "";
}
/// <summary>
/// A revision of a page
/// </summary>
public class PageRevision : Revision
{
/// <summary>
/// The ID of the page to which this revision belongs
/// </summary>
public string PageId { get; set; } = "";
/// <summary>
/// The page to which this revision belongs
/// </summary>
public Page Page { get; set; } = default!;
}
/// <summary>
/// A revision of a post
/// </summary>
public class PostRevision : Revision
{
/// <summary>
/// The ID of the post to which this revision applies
/// </summary>
public string PostId { get; set; } = "";
/// <summary>
/// The post to which this revision applies
/// </summary>
public Post Post { get; set; } = default!;
}

17
src/MyWebLog.Data/Tag.cs Normal file
View File

@ -0,0 +1,17 @@
namespace MyWebLog.Data;
/// <summary>
/// A tag
/// </summary>
public class Tag
{
/// <summary>
/// The name of the tag
/// </summary>
public string Name { get; set; } = "";
/// <summary>
/// The posts with this tag assigned
/// </summary>
public ICollection<Post> Posts { get; set; } = default!;
}

View File

@ -0,0 +1,69 @@
using Microsoft.EntityFrameworkCore;
namespace MyWebLog.Data;
/// <summary>
/// Data context for web log data
/// </summary>
public sealed class WebLogDbContext : DbContext
{
/// <summary>
/// The categories for the web log
/// </summary>
public DbSet<Category> Categories { get; set; } = default!;
/// <summary>
/// Comments on posts
/// </summary>
public DbSet<Comment> Comments { get; set; } = default!;
/// <summary>
/// Pages
/// </summary>
public DbSet<Page> Pages { get; set; } = default!;
/// <summary>
/// Web log posts
/// </summary>
public DbSet<Post> Posts { get; set; } = default!;
/// <summary>
/// Post tags
/// </summary>
public DbSet<Tag> Tags { get; set; } = default!;
/// <summary>
/// The users of the web log
/// </summary>
public DbSet<WebLogUser> Users { get; set; } = default!;
/// <summary>
/// The details for the web log
/// </summary>
public DbSet<WebLogDetails> WebLogDetails { get; set; } = default!;
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Configuration options</param>
public WebLogDbContext(DbContextOptions<WebLogDbContext> options) : base(options) { }
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Tag and WebLogDetails use Name as its ID
modelBuilder.Entity<Tag>().HasKey(t => t.Name);
modelBuilder.Entity<WebLogDetails>().HasKey(wld => wld.Name);
// Index slugs and links
modelBuilder.Entity<Category>().HasIndex(c => c.Slug);
modelBuilder.Entity<Page>().HasIndex(p => p.Permalink);
modelBuilder.Entity<Post>().HasIndex(p => p.Permalink);
// Link "author" to "user"
modelBuilder.Entity<Page>().HasOne(p => p.Author).WithMany(wbu => wbu.Pages).HasForeignKey(p => p.AuthorId);
modelBuilder.Entity<Post>().HasOne(p => p.Author).WithMany(wbu => wbu.Posts).HasForeignKey(p => p.AuthorId);
}
}

View File

@ -0,0 +1,37 @@
namespace MyWebLog.Data;
/// <summary>
/// The details about a web log
/// </summary>
public class WebLogDetails
{
/// <summary>
/// The name of the web log
/// </summary>
public string Name { get; set; } = "";
/// <summary>
/// A subtitle for the web log
/// </summary>
public string? Subtitle { get; set; } = null;
/// <summary>
/// The default page ("posts" or a page Id)
/// </summary>
public string DefaultPage { get; set; } = "";
/// <summary>
/// The path of the theme (within /views/themes)
/// </summary>
public string ThemePath { get; set; } = "";
/// <summary>
/// The URL base
/// </summary>
public string UrlBase { get; set; } = "";
/// <summary>
/// The time zone in which dates/times should be displayed
/// </summary>
public string TimeZone { get; set; } = "";
}

View File

@ -0,0 +1,57 @@
namespace MyWebLog.Data;
/// <summary>
/// A user of the web log
/// </summary>
public class WebLogUser
{
/// <summary>
/// The ID of the user
/// </summary>
public string Id { get; set; } = "";
/// <summary>
/// The user name (e-mail address)
/// </summary>
public string UserName { get; set; } = "";
/// <summary>
/// The user's first name
/// </summary>
public string FirstName { get; set; } = "";
/// <summary>
/// The user's last name
/// </summary>
public string LastName { get; set; } = "";
/// <summary>
/// The user's preferred name
/// </summary>
public string PreferredName { get; set; } = "";
/// <summary>
/// The hash of the user's password
/// </summary>
public string PasswordHash { get; set; } = "";
/// <summary>
/// Salt used to calculate the user's password hash
/// </summary>
public Guid Salt { get; set; } = Guid.Empty;
/// <summary>
/// The URL of the user's personal site
/// </summary>
public string? Url { get; set; } = null;
/// <summary>
/// Pages written by this author
/// </summary>
public ICollection<Page> Pages { get; set; } = default!;
/// <summary>
/// Posts written by this author
/// </summary>
public ICollection<Post> Posts { get; set; } = default!;
}

31
src/MyWebLog.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MyWebLog", "MyWebLog\MyWebLog.fsproj", "{2E5E2346-25FE-4CBD-89AA-6148A33DE09C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWebLog.Data", "MyWebLog.Data\MyWebLog.Data.csproj", "{0177C744-F913-4352-A0EC-478B4B0388C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2E5E2346-25FE-4CBD-89AA-6148A33DE09C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E5E2346-25FE-4CBD-89AA-6148A33DE09C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E5E2346-25FE-4CBD-89AA-6148A33DE09C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E5E2346-25FE-4CBD-89AA-6148A33DE09C}.Release|Any CPU.Build.0 = Release|Any CPU
{0177C744-F913-4352-A0EC-478B4B0388C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0177C744-F913-4352-A0EC-478B4B0388C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0177C744-F913-4352-A0EC-478B4B0388C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0177C744-F913-4352-A0EC-478B4B0388C3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {70EEF0F4-8709-44A8-AD9B-24D1EB84CFB4}
EndGlobalSection
EndGlobal