Skip to content

Commit afb8824

Browse files
committed
Use OPEN_JSON for persisting tags
1 parent f7771ad commit afb8824

File tree

18 files changed

+59
-121
lines changed

18 files changed

+59
-121
lines changed

src/LinkDotNet.Blog.Domain/BlogPost.cs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.Immutable;
34
using System.Linq;
45

56
namespace LinkDotNet.Blog.Domain;
@@ -24,15 +25,15 @@ private BlogPost()
2425

2526
public DateTime? ScheduledPublishDate { get; private set; }
2627

27-
public ICollection<Tag> Tags { get; private set; }
28+
public ICollection<string> Tags { get; private set; }
2829

2930
public bool IsPublished { get; private set; }
3031

3132
public int Likes { get; set; }
3233

3334
public bool IsScheduled => ScheduledPublishDate is not null;
3435

35-
public string TagsAsString => Tags is null ? string.Empty : string.Join(", ", Tags.Select(t => t.Content));
36+
public string TagsAsString => Tags is null ? string.Empty : string.Join(", ", Tags);
3637

3738
public static BlogPost Create(
3839
string title,
@@ -62,7 +63,7 @@ public static BlogPost Create(
6263
PreviewImageUrl = previewImageUrl,
6364
PreviewImageUrlFallback = previewImageUrlFallback,
6465
IsPublished = isPublished,
65-
Tags = tags?.Select(Tag.Create).ToList(),
66+
Tags = tags?.Select(t => t.Trim()).ToImmutableArray(),
6667
};
6768

6869
return blogPost;
@@ -89,22 +90,6 @@ public void Update(BlogPost from)
8990
PreviewImageUrl = from.PreviewImageUrl;
9091
PreviewImageUrlFallback = from.PreviewImageUrlFallback;
9192
IsPublished = from.IsPublished;
92-
ReplaceTags(from.Tags);
93-
}
94-
95-
private void ReplaceTags(IEnumerable<Tag> tags)
96-
{
97-
Tags?.Clear();
98-
if (Tags == null || tags == null)
99-
{
100-
Tags = tags?.ToList();
101-
}
102-
else
103-
{
104-
foreach (var tag in tags)
105-
{
106-
Tags.Add(tag);
107-
}
108-
}
93+
Tags = from.Tags;
10994
}
11095
}

src/LinkDotNet.Blog.Domain/Tag.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/BlogDbContext.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ public BlogDbContext(DbContextOptions options)
1414

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

17-
public DbSet<Tag> Tags { get; set; }
18-
1917
public DbSet<ProfileInformationEntry> ProfileInformationEntries { get; set; }
2018

2119
public DbSet<Skill> Skills { get; set; }
@@ -27,7 +25,6 @@ public BlogDbContext(DbContextOptions options)
2725
protected override void OnModelCreating(ModelBuilder modelBuilder)
2826
{
2927
modelBuilder.ApplyConfiguration(new BlogPostConfiguration());
30-
modelBuilder.ApplyConfiguration(new TagsConfiguration());
3128
modelBuilder.ApplyConfiguration(new ProfileInformationEntryConfiguration());
3229
modelBuilder.ApplyConfiguration(new SkillConfiguration());
3330
modelBuilder.ApplyConfiguration(new UserRecordConfiguration());

src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/BlogPostConfiguration.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ public void Configure(EntityTypeBuilder<BlogPost> builder)
1010
{
1111
builder.HasKey(c => c.Id);
1212
builder.Property(c => c.Id).ValueGeneratedOnAdd();
13-
14-
builder.HasMany(t => t.Tags)
15-
.WithOne()
16-
.OnDelete(DeleteBehavior.Cascade);
17-
builder.Navigation(x => x.Tags).AutoInclude();
1813
builder.Property(x => x.Title).HasMaxLength(256).IsRequired();
1914
builder.Property(x => x.PreviewImageUrl).HasMaxLength(1024).IsRequired();
2015
builder.Property(x => x.PreviewImageUrlFallback).HasMaxLength(1024);

src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Mapping/TagsConfiguration.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/LinkDotNet.Blog.Infrastructure/Persistence/Sql/Repository.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,21 @@ public async ValueTask StoreAsync(TEntity entity)
6969
}
7070
else
7171
{
72-
blogDbContext.Entry(entity).State = EntityState.Modified;
72+
var dbEntity = blogDbContext.Set<TEntity>().Local.FirstOrDefault(e => e.Id == entity.Id);
73+
if (dbEntity != null)
74+
{
75+
blogDbContext.Entry(dbEntity).CurrentValues.SetValues(entity);
76+
blogDbContext.Entry(dbEntity).State = EntityState.Modified;
77+
}
78+
else
79+
{
80+
blogDbContext.Entry(entity).State = EntityState.Modified;
81+
}
7382
}
7483

7584
await blogDbContext.SaveChangesAsync();
7685
}
77-
86+
7887
public async ValueTask DeleteAsync(string id)
7988
{
8089
var entityToDelete = await GetByIdAsync(id);

src/LinkDotNet.Blog.Web/Controller/RssFeedController.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ private static SyndicationItem CreateSyndicationItemFromBlogPost(string url, Blo
8686

8787
private static void AddCategories(ICollection<SyndicationCategory> categories, BlogPostRssInfo blogPost)
8888
{
89-
foreach (var tag in blogPost.Tags ?? Array.Empty<Tag>())
89+
foreach (var tag in blogPost.Tags ?? Array.Empty<string>())
9090
{
91-
categories.Add(new SyndicationCategory(tag.Content));
91+
categories.Add(new SyndicationCategory(tag));
9292
}
9393
}
9494

@@ -107,5 +107,5 @@ private sealed record BlogPostRssInfo(
107107
string ShortDescription,
108108
DateTime UpdatedDate,
109109
string PreviewImageUrl,
110-
ICollection<Tag> Tags);
110+
ICollection<string> Tags);
111111
}

src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/Services/SitemapService.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ private IEnumerable<SitemapUrl> CreateUrlsForTags(IEnumerable<BlogPost> blogPost
5757
{
5858
return blogPosts
5959
.SelectMany(b => b.Tags)
60-
.Select(t => t.Content)
6160
.Distinct()
6261
.Select(t => new SitemapUrl
6362
{

src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{
2929
<li class="tags me-4">
3030
<ul>
31-
@foreach (var tag in BlogPost.Tags.Select(t => t.Content))
31+
@foreach (var tag in BlogPost.Tags)
3232
{
3333
<li><a class="goto-tag" href="/searchByTag/@(Uri.EscapeDataString(tag))">@tag</a></li>
3434
}

src/LinkDotNet.Blog.Web/Features/Search/SearchPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
{
2525
var term = Uri.UnescapeDataString(SearchTerm);
2626
blogPosts = await BlogPostRepository.GetAllAsync(t => t.IsPublished && (t.Title.Contains(term)
27-
|| t.Tags.Any(tag => tag.Content == term)),
27+
|| t.Tags.Any(tag => tag == term)),
2828
b => b.UpdatedDate);
2929
}
3030

src/LinkDotNet.Blog.Web/Features/SearchByTag/SearchByTagPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
{
2424
Tag = Uri.UnescapeDataString(Tag);
2525
blogPosts = await BlogPostRepository.GetAllAsync(
26-
b => b.IsPublished && b.Tags.Any(t => t.Content == Tag),
26+
b => b.IsPublished && b.Tags.Any(t => t == Tag),
2727
b => b.UpdatedDate);
2828
}
2929

src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ else
3737
@if (BlogPost.Tags != null && BlogPost.Tags.Any())
3838
{
3939
<span class="blogpost-tag d-inline-block">
40-
@foreach (var tag in BlogPost.Tags.Select(t => t.Content))
40+
@foreach (var tag in BlogPost.Tags)
4141
{
4242
<a class="goto-tag ms-1" href="/searchByTag/@(Uri.EscapeDataString(tag))">@tag</a>
4343
}

tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/CachedRepositoryTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public async Task ShouldNotCacheWhenDifferentQueries()
2020
await Repository.StoreAsync(bp3);
2121
var searchTerm = "tag 1";
2222
var sut = new CachedRepository<BlogPost>(Repository, new MemoryCache(new MemoryCacheOptions()));
23-
await sut.GetAllAsync(f => f.Tags.Any(t => t.Content == searchTerm));
23+
await sut.GetAllAsync(f => f.Tags.Any(t => t == searchTerm));
2424
searchTerm = "tag 2";
2525

26-
var allWithTag2 = await sut.GetAllAsync(f => f.Tags.Any(t => t.Content == searchTerm));
26+
var allWithTag2 = await sut.GetAllAsync(f => f.Tags.Any(t => t == searchTerm));
2727

2828
allWithTag2.Count.Should().Be(1);
29-
allWithTag2.Single().Tags.Single().Content.Should().Be("tag 2");
29+
allWithTag2.Single().Tags.Single().Should().Be("tag 2");
3030
}
3131

3232
[Fact]

tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/RavenDb/BlogPostRepositoryTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public async Task ShouldLoadBlogPost()
3737
blogPostFromRepo.PreviewImageUrl.Should().Be("url");
3838
blogPostFromRepo.IsPublished.Should().BeTrue();
3939
blogPostFromRepo.Tags.Should().HaveCount(2);
40-
var tagContent = blogPostFromRepo.Tags.Select(t => t.Content).ToList();
40+
var tagContent = blogPostFromRepo.Tags;
4141
tagContent.Should().Contain(new[] { "Tag 1", "Tag 2" });
4242
}
4343

@@ -96,7 +96,7 @@ public async Task ShouldSaveBlogPost()
9696
blogPostFromContext.IsPublished.Should().BeTrue();
9797
blogPostFromContext.PreviewImageUrl.Should().Be("url");
9898
blogPostFromContext.Tags.Should().HaveCount(2);
99-
var tagContent = blogPostFromContext.Tags.Select(t => t.Content).ToList();
99+
var tagContent = blogPostFromContext.Tags;
100100
tagContent.Should().Contain(new[] { "Tag 1", "Tag 2" });
101101
}
102102

@@ -117,7 +117,7 @@ public async Task ShouldGetAllBlogPosts()
117117
blogPostFromRepo.PreviewImageUrl.Should().Be("url");
118118
blogPostFromRepo.IsPublished.Should().BeTrue();
119119
blogPostFromRepo.Tags.Should().HaveCount(2);
120-
var tagContent = blogPostFromRepo.Tags.Select(t => t.Content).ToList();
120+
var tagContent = blogPostFromRepo.Tags;
121121
tagContent.Should().Contain(new[] { "Tag 1", "Tag 2" });
122122
}
123123

tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System.Linq;
22
using System.Threading.Tasks;
33
using LinkDotNet.Blog.Domain;
4+
using LinkDotNet.Blog.Infrastructure.Persistence;
45
using LinkDotNet.Blog.TestUtilities;
56
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.Extensions.Caching.Memory;
68

79
namespace LinkDotNet.Blog.IntegrationTests.Infrastructure.Persistence.Sql;
810

@@ -24,7 +26,7 @@ public async Task ShouldLoadBlogPost()
2426
blogPostFromRepo.PreviewImageUrl.Should().Be("url");
2527
blogPostFromRepo.IsPublished.Should().BeTrue();
2628
blogPostFromRepo.Tags.Should().HaveCount(2);
27-
var tagContent = blogPostFromRepo.Tags.Select(t => t.Content).ToList();
29+
var tagContent = blogPostFromRepo.Tags;
2830
tagContent.Should().Contain(new[] { "Tag 1", "Tag 2" });
2931
}
3032

@@ -35,15 +37,15 @@ public async Task ShouldSaveBlogPost()
3537

3638
await Repository.StoreAsync(blogPost);
3739

38-
var blogPostFromContext = await DbContext.BlogPosts.Include(b => b.Tags).AsNoTracking().SingleOrDefaultAsync(s => s.Id == blogPost.Id);
40+
var blogPostFromContext = await DbContext.BlogPosts.AsNoTracking().SingleOrDefaultAsync(s => s.Id == blogPost.Id);
3941
blogPostFromContext.Should().NotBeNull();
4042
blogPostFromContext.Title.Should().Be("Title");
4143
blogPostFromContext.ShortDescription.Should().Be("Subtitle");
4244
blogPostFromContext.Content.Should().Be("Content");
4345
blogPostFromContext.IsPublished.Should().BeTrue();
4446
blogPostFromContext.PreviewImageUrl.Should().Be("url");
4547
blogPostFromContext.Tags.Should().HaveCount(2);
46-
var tagContent = blogPostFromContext.Tags.Select(t => t.Content).ToList();
48+
var tagContent = blogPostFromContext.Tags;
4749
tagContent.Should().Contain(new[] { "Tag 1", "Tag 2" });
4850
}
4951

@@ -65,7 +67,7 @@ public async Task ShouldGetAllBlogPosts()
6567
blogPostFromRepo.PreviewImageUrl.Should().Be("url");
6668
blogPostFromRepo.IsPublished.Should().BeTrue();
6769
blogPostFromRepo.Tags.Should().HaveCount(2);
68-
var tagContent = blogPostFromRepo.Tags.Select(t => t.Content).ToList();
70+
var tagContent = blogPostFromRepo.Tags;
6971
tagContent.Should().Contain(new[] { "Tag 1", "Tag 2" });
7072
}
7173

@@ -116,4 +118,20 @@ public async Task ShouldDelete()
116118

117119
(await DbContext.BlogPosts.AsNoTracking().AnyAsync(b => b.Id == blogPost.Id)).Should().BeFalse();
118120
}
121+
122+
[Fact]
123+
public async Task GivenBlogPostWithTags_WhenLoadingAndDeleting_ThenShouldBeUpdated()
124+
{
125+
var bp = new BlogPostBuilder().WithTags("tag 1").Build();
126+
var sut = new CachedRepository<BlogPost>(Repository, new MemoryCache(new MemoryCacheOptions()));
127+
await sut.StoreAsync(bp);
128+
var updateBp = new BlogPostBuilder().WithTags("tag 2").Build();
129+
var bpFromCache = await sut.GetByIdAsync(bp.Id);
130+
bpFromCache.Update(updateBp);
131+
await sut.StoreAsync(bpFromCache);
132+
133+
var bpFromDb = await sut.GetByIdAsync(bp.Id);
134+
135+
bpFromDb.Tags.Single().Should().Be("tag 2");
136+
}
119137
}

tests/LinkDotNet.Blog.UnitTests/Domain/BlogPostTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@ public void ShouldUpdateTagsWhenExisting()
3737
blogPostToUpdate.Update(blogPost);
3838

3939
blogPostToUpdate.Tags.Should().HaveCount(1);
40-
blogPostToUpdate.Tags.Single().Content.Should().Be("tag 2");
40+
blogPostToUpdate.Tags.Single().Should().Be("tag 2");
4141
}
4242

4343
[Fact]
4444
public void ShouldTrimWhitespacesFromTags()
4545
{
4646
var blogPost = BlogPost.Create("Title", "Sub", "Content", "Preview", false, tags: new[] { " Tag 1", " Tag 2 ", });
4747

48-
blogPost.Tags.Select(t => t.Content).Should().Contain("Tag 1");
49-
blogPost.Tags.Select(t => t.Content).Should().Contain("Tag 2");
48+
blogPost.Tags.Should().Contain("Tag 1");
49+
blogPost.Tags.Should().Contain("Tag 2");
5050
}
5151

5252
[Fact]
@@ -67,7 +67,7 @@ public void ShouldNotDeleteTagsWhenSameReference()
6767
bp.Update(bp);
6868

6969
bp.Tags.Should().HaveCount(1);
70-
bp.Tags.Single().Content.Should().Be("tag 1");
70+
bp.Tags.Single().Should().Be("tag 1");
7171
}
7272

7373
[Fact]

tests/LinkDotNet.Blog.UnitTests/Domain/TagTests.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)