Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions src/LinkDotNet.Blog.Domain/BlogPost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public sealed partial class BlogPost : Entity

public int Likes { get; set; }

public int Order { get; set; }

public bool IsScheduled => ScheduledPublishDate is not null;

public string TagsAsString => string.Join(",", Tags);
Expand All @@ -40,6 +42,10 @@ public sealed partial class BlogPost : Entity

public string? AuthorName { get; private set; }

public string? SeriesId { get; private set; }

public Series? Series { get; private set; }

private string GenerateSlug()
{
if (string.IsNullOrWhiteSpace(Title))
Expand Down Expand Up @@ -95,7 +101,9 @@ public static BlogPost Create(
DateTime? scheduledPublishDate = null,
IEnumerable<string>? tags = null,
string? previewImageUrlFallback = null,
string? authorName = null)
string? authorName = null,
string? seriesId = null,
int order = 0)
{
if (scheduledPublishDate is not null && isPublished)
{
Expand All @@ -116,7 +124,9 @@ public static BlogPost Create(
IsPublished = isPublished,
Tags = tags?.Select(t => t.Trim()).ToImmutableArray() ?? [],
ReadingTimeInMinutes = ReadingTimeCalculator.CalculateReadingTime(content),
AuthorName = authorName
AuthorName = authorName,
SeriesId = seriesId,
Order = order
};

return blogPost;
Expand All @@ -128,6 +138,22 @@ public void Publish()
IsPublished = true;
}

public void RemoveFromSeries()
{
SeriesId = null;
Series = null;
Order = 0;
}

public void AssignToSeries(string seriesId, int order)
{
ArgumentException.ThrowIfNullOrEmpty(seriesId);

SeriesId = seriesId;
Series = null;
Order = order;
}

public void Update(BlogPost from)
{
ArgumentNullException.ThrowIfNull(from);
Expand All @@ -148,5 +174,7 @@ public void Update(BlogPost from)
Tags = from.Tags;
ReadingTimeInMinutes = from.ReadingTimeInMinutes;
AuthorName = from.AuthorName;
SeriesId = from.SeriesId;
Order = from.Order;
}
}
8 changes: 8 additions & 0 deletions src/LinkDotNet.Blog.Domain/Series.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace LinkDotNet.Blog.Domain;

public sealed class Series : Entity
{
public string Name { get; set; } = default!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,20 @@
{
var filter = Builders<TEntity>.Filter.Eq(e => e.Id, id);
using var result = await Collection.FindAsync(filter);
return await result.FirstOrDefaultAsync();
var entity = await result.FirstOrDefaultAsync();

if (entity is BlogPost { SeriesId: not null } blogPost)
{
var seriesCollection = database.GetCollection<Series>(nameof(Series));
var seriesFilter = Builders<Series>.Filter.Eq(s => s.Id, blogPost.SeriesId);
var seriesResult = await seriesCollection.FindAsync(seriesFilter);

Check failure on line 49 in src/LinkDotNet.Blog.Infrastructure/Persistence/MongoDB/Repository.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Check failure on line 49 in src/LinkDotNet.Blog.Infrastructure/Persistence/MongoDB/Repository.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Check failure on line 49 in src/LinkDotNet.Blog.Infrastructure/Persistence/MongoDB/Repository.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Check failure on line 49 in src/LinkDotNet.Blog.Infrastructure/Persistence/MongoDB/Repository.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

var series = await seriesResult.FirstOrDefaultAsync();

var seriesProperty = typeof(BlogPost).GetProperty(nameof(BlogPost.Series));
seriesProperty?.SetValue(blogPost, series);
}

return entity;
}

public async ValueTask<IPagedList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>>? filter = null,
Expand Down Expand Up @@ -105,9 +118,9 @@
{
ArgumentNullException.ThrowIfNull(records);

if (records.Count != 0)
foreach (var record in records)
{
await Collection.InsertManyAsync(records);
await StoreAsync(record);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ public async ValueTask<HealthCheckResult> PerformHealthCheckAsync()
public async ValueTask<TEntity?> GetByIdAsync(string id)
{
using var session = documentStore.OpenAsyncSession();
return await session.LoadAsync<TEntity>(id);
var entity = await session.LoadAsync<TEntity>(id);

if (entity is BlogPost { SeriesId: not null } blogPost)
{
var series = await session.LoadAsync<Series>(blogPost.SeriesId);
var seriesProperty = typeof(BlogPost).GetProperty(nameof(BlogPost.Series));
seriesProperty?.SetValue(blogPost, series);
}

return entity;
}

public ValueTask<IPagedList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>>? filter = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public BlogDbContext(DbContextOptions options)

public DbSet<BlogPost> BlogPosts { get; set; }

public DbSet<Series> Series { get; set; }

public DbSet<ProfileInformationEntry> ProfileInformationEntries { get; set; }

public DbSet<Skill> Skills { get; set; }
Expand All @@ -38,6 +40,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
ArgumentNullException.ThrowIfNull(modelBuilder);

modelBuilder.ApplyConfiguration(new BlogPostConfiguration());
modelBuilder.ApplyConfiguration(new SeriesConfiguration());
modelBuilder.ApplyConfiguration(new BlogPostRecordConfiguration());
modelBuilder.ApplyConfiguration(new BlogPostTemplateConfiguration());
modelBuilder.ApplyConfiguration(new BlogPostVersionConfiguration());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ public void Configure(EntityTypeBuilder<BlogPost> builder)
builder.Property(x => x.ShortDescription).IsRequired();
builder.Property(x => x.Likes).IsRequired();
builder.Property(x => x.IsPublished).IsRequired();
builder.Property(x => x.Order).IsRequired();

builder.Property(x => x.Tags).HasMaxLength(2048);
builder.Property(x => x.AuthorName).HasMaxLength(256).IsRequired(false);
builder.Property(x => x.SeriesId).IsUnicode(false).IsRequired(false);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might wanna have a db constraint that order has to be unique

builder.HasOne(x => x.Series)
.WithMany()
.HasForeignKey(x => x.SeriesId)
.OnDelete(DeleteBehavior.SetNull);

builder.HasIndex(x => new { x.IsPublished, x.UpdatedDate })
.HasDatabaseName("IX_BlogPosts_IsPublished_UpdatedDate")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using LinkDotNet.Blog.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace LinkDotNet.Blog.Infrastructure.Persistence.Sql.Mapping;

internal sealed class SeriesConfiguration : IEntityTypeConfiguration<Series>
{
public void Configure(EntityTypeBuilder<Series> builder)
{
builder.HasKey(c => c.Id);
builder.Property(c => c.Id)
.IsUnicode(false)
.ValueGeneratedOnAdd();
builder.Property(x => x.Name).HasMaxLength(256).IsRequired();
}
}
19 changes: 17 additions & 2 deletions src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@ public async ValueTask<HealthCheckResult> PerformHealthCheckAsync()
public async ValueTask<TEntity?> GetByIdAsync(string id)
{
await using var blogDbContext = await dbContextFactory.CreateDbContextAsync();
return await blogDbContext.Set<TEntity>().FirstOrDefaultAsync(b => b.Id == id);
var query = blogDbContext.Set<TEntity>().AsQueryable();

if (typeof(TEntity) == typeof(BlogPost))
{
query = query.Include("Series");
}

return await query.FirstOrDefaultAsync(b => b.Id == id);
}

public ValueTask<IPagedList<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>>? filter = null,
Expand Down Expand Up @@ -150,7 +157,15 @@ async Task StoreBulkAsyncInBatchesAsync()
var count = 0;
foreach (var record in records)
{
await blogDbContext.Set<TEntity>().AddAsync(record);
if (string.IsNullOrEmpty(record.Id))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if that breaks now the way in SimilarBlogPostJob - we deleted first and then insert. With the same Ids. Might lead to an exception in SQLServer. Have to check

{
await blogDbContext.Set<TEntity>().AddAsync(record);
}
else
{
blogDbContext.Entry(record).State = EntityState.Modified;
}

if (++count % 1000 == 0)
{
LogBatch(count);
Expand Down
Loading
Loading