Skip to content

Commit 7c362e1

Browse files
committed
refactor: use expression-bodied members for projection builder constructors and improve code style in projections.
1 parent c7a508c commit 7c362e1

7 files changed

Lines changed: 249 additions & 217 deletions

File tree

src/ApiService/BookStore.ApiService/Endpoints/AuthorEndpoints.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Globalization;
12
using BookStore.ApiService.Infrastructure;
23
using BookStore.ApiService.Models;
34
using BookStore.ApiService.Projections;
@@ -6,7 +7,6 @@
67
using Microsoft.AspNetCore.Http.HttpResults;
78
using Microsoft.AspNetCore.Mvc;
89
using Microsoft.Extensions.Options;
9-
using System.Globalization;
1010

1111
namespace BookStore.ApiService.Endpoints;
1212

@@ -65,7 +65,7 @@ static async Task<Results<Ok<AuthorDto>, NotFound>> GetAuthor(
6565
{
6666
var culture = CultureInfo.CurrentCulture.Name;
6767
await using var session = store.QuerySession(culture);
68-
68+
6969
var author = await session.LoadAsync<AuthorProjection>(id);
7070
if (author == null)
7171
{

src/ApiService/BookStore.ApiService/Endpoints/BookEndpoints.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Globalization;
12
using BookStore.ApiService.Models;
23
using BookStore.ApiService.Projections;
34
using Marten;
@@ -6,7 +7,6 @@
67
using Microsoft.AspNetCore.Mvc;
78
using Microsoft.Extensions.Options;
89
using Npgsql;
9-
using System.Globalization;
1010

1111
namespace BookStore.ApiService.Endpoints;
1212

@@ -40,7 +40,7 @@ static async Task<Ok<PagedListDto<BookDto>>> SearchBooks(
4040
[FromQuery] string? search = null)
4141
{
4242
var paging = request.Normalize(paginationOptions.Value);
43-
43+
4444
// Use the resolved culture as the tenant ID
4545
var culture = CultureInfo.CurrentCulture.Name; // e.g. "en-US", "pt", "en"
4646
await using var session = store.QuerySession(culture);
@@ -180,6 +180,4 @@ [.. book.CategoryIds
180180

181181
return TypedResults.Ok(bookDto);
182182
}
183-
184-
185183
}

src/ApiService/BookStore.ApiService/Endpoints/CategoryEndpoints.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Globalization;
12
using BookStore.ApiService.Infrastructure;
23
using BookStore.ApiService.Models;
34
using BookStore.ApiService.Projections;
@@ -6,7 +7,6 @@
67
using Microsoft.AspNetCore.Http.HttpResults;
78
using Microsoft.AspNetCore.Mvc;
89
using Microsoft.Extensions.Options;
9-
using System.Globalization;
1010

1111
namespace BookStore.ApiService.Endpoints;
1212

@@ -64,7 +64,7 @@ static async Task<Results<Ok<CategoryDto>, NotFound>> GetCategory(
6464
{
6565
var culture = CultureInfo.CurrentCulture.Name;
6666
await using var session = store.QuerySession(culture);
67-
67+
6868
var category = await session.LoadAsync<CategoryProjection>(id);
6969
if (category == null)
7070
{

src/ApiService/BookStore.ApiService/Infrastructure/Extensions/MartenConfigurationExtensions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,18 @@ static void RegisterEventTypes(StoreOptions options)
101101
static void RegisterProjections(StoreOptions options, IServiceProvider sp)
102102
{
103103
// Configure Conjoined Tenancy for Multi-Lingual Projections
104-
options.Schema.For<BookSearchProjection>().MultiTenanted();
105-
options.Schema.For<AuthorProjection>().MultiTenanted();
106-
options.Schema.For<CategoryProjection>().MultiTenanted();
104+
_ = options.Schema.For<BookSearchProjection>().MultiTenanted();
105+
_ = options.Schema.For<AuthorProjection>().MultiTenanted();
106+
_ = options.Schema.For<CategoryProjection>().MultiTenanted();
107107

108108
// Configure projections - using AddAsync for async projections managed by Wolverine
109109
// Instantiate builders using ServiceProvider to satisfy dependencies
110110
var localization = sp.GetRequiredService<IOptions<LocalizationOptions>>();
111-
111+
112112
options.Projections.Add(new AuthorProjectionBuilder(localization), ProjectionLifecycle.Async);
113113
options.Projections.Add(new CategoryProjectionBuilder(localization), ProjectionLifecycle.Async);
114114
options.Projections.Add(new BookSearchProjectionBuilder(localization), ProjectionLifecycle.Async);
115-
115+
116116
// PublisherProjectionBuilder has no dependencies, so simple Add is fine if it has parameterless ctor
117117
// Or we can just use defaults if it does. Check PublisherProjectionBuilder.
118118
// Assuming it's simple:

src/ApiService/BookStore.ApiService/Projections/AuthorProjection.cs

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using BookStore.ApiService.Events;
22
using BookStore.ApiService.Models;
3+
using JasperFx.Events;
34
using Marten;
45
using Marten.Events;
56
using Marten.Events.Projections;
67
using Microsoft.Extensions.Options;
7-
using JasperFx.Events;
88

99
namespace BookStore.ApiService.Projections;
1010

@@ -21,90 +21,103 @@ public class AuthorProjectionBuilder : EventProjection
2121
{
2222
private readonly LocalizationOptions _localization;
2323

24-
public AuthorProjectionBuilder(IOptions<LocalizationOptions> localizationOptions)
25-
{
26-
_localization = localizationOptions.Value;
27-
}
28-
24+
public AuthorProjectionBuilder(IOptions<LocalizationOptions> localizationOptions)
25+
=> _localization = localizationOptions.Value;
26+
2927
public async Task Project(IEvent<AuthorAdded> @event, IDocumentOperations ops)
3028
{
31-
var author = @event.Data;
32-
if (ops is IDocumentSession session)
33-
{
34-
foreach (var culture in _localization.SupportedCultures)
35-
{
36-
using var tenantSession = session.ForTenant(culture);
37-
38-
var projection = new AuthorProjection
39-
{
40-
Id = author.Id,
41-
Name = author.Name,
42-
Biography = GetLocalizedBio(author.Translations, culture),
43-
ProjectionLocale = culture,
44-
LastModified = author.Timestamp
45-
};
46-
tenantSession.Store(projection);
47-
// await tenantSession.SaveChangesAsync(); // Auto-saved by parent session batch
48-
}
49-
}
29+
var author = @event.Data;
30+
if (ops is IDocumentSession session)
31+
{
32+
foreach (var culture in _localization.SupportedCultures)
33+
{
34+
using var tenantSession = session.ForTenant(culture);
35+
36+
var projection = new AuthorProjection
37+
{
38+
Id = author.Id,
39+
Name = author.Name,
40+
Biography = GetLocalizedBio(author.Translations, culture),
41+
ProjectionLocale = culture,
42+
LastModified = author.Timestamp
43+
};
44+
tenantSession.Store(projection);
45+
// await tenantSession.SaveChangesAsync(); // Auto-saved by parent session batch
46+
}
47+
}
5048
}
5149

5250
public async Task Project(IEvent<AuthorUpdated> @event, IDocumentOperations ops)
5351
{
54-
var author = @event.Data;
55-
if (ops is IDocumentSession session)
56-
{
57-
foreach (var culture in _localization.SupportedCultures)
58-
{
59-
using var tenantSession = session.ForTenant(culture);
60-
61-
var projection = new AuthorProjection
62-
{
63-
Id = author.Id,
64-
Name = author.Name,
65-
Biography = GetLocalizedBio(author.Translations, culture),
66-
ProjectionLocale = culture,
67-
LastModified = author.Timestamp
68-
};
69-
tenantSession.Store(projection);
70-
// await tenantSession.SaveChangesAsync();
71-
}
72-
}
52+
var author = @event.Data;
53+
if (ops is IDocumentSession session)
54+
{
55+
foreach (var culture in _localization.SupportedCultures)
56+
{
57+
using var tenantSession = session.ForTenant(culture);
58+
59+
var projection = new AuthorProjection
60+
{
61+
Id = author.Id,
62+
Name = author.Name,
63+
Biography = GetLocalizedBio(author.Translations, culture),
64+
ProjectionLocale = culture,
65+
LastModified = author.Timestamp
66+
};
67+
tenantSession.Store(projection);
68+
// await tenantSession.SaveChangesAsync();
69+
}
70+
}
7371
}
74-
72+
7573
public async Task Project(IEvent<AuthorSoftDeleted> @event, IDocumentOperations ops)
7674
{
7775
if (ops is IDocumentSession session)
7876
{
7977
foreach (var culture in _localization.SupportedCultures)
8078
{
81-
using var tenantSession = session.ForTenant(culture);
82-
tenantSession.DeleteWhere<AuthorProjection>(x => x.Id == @event.Data.Id);
83-
// await tenantSession.SaveChangesAsync();
79+
using var tenantSession = session.ForTenant(culture);
80+
tenantSession.DeleteWhere<AuthorProjection>(x => x.Id == @event.Data.Id);
81+
// await tenantSession.SaveChangesAsync();
8482
}
8583
}
8684
}
87-
85+
8886
private string GetLocalizedBio(Dictionary<string, AuthorTranslation>? translations, string culture)
8987
{
90-
if (translations == null || translations.Count == 0) return string.Empty;
88+
if (translations == null || translations.Count == 0)
89+
{
90+
return string.Empty;
91+
}
9192

9293
var cultureInfo = System.Globalization.CultureInfo.GetCultureInfo(culture);
9394

9495
// 1. Exact
95-
if (translations.TryGetValue(culture, out var exact)) return exact.Biography ?? string.Empty;
96-
96+
if (translations.TryGetValue(culture, out var exact))
97+
{
98+
return exact.Biography ?? string.Empty;
99+
}
100+
97101
// 2. Neutral
98-
if (translations.TryGetValue(cultureInfo.TwoLetterISOLanguageName, out var neutral)) return neutral.Biography ?? string.Empty;
99-
102+
if (translations.TryGetValue(cultureInfo.TwoLetterISOLanguageName, out var neutral))
103+
{
104+
return neutral.Biography ?? string.Empty;
105+
}
106+
100107
// 3. Default Culture
101108
var defaultCulture = _localization.DefaultCulture;
102-
if (translations.TryGetValue(defaultCulture, out var def)) return def.Biography ?? string.Empty;
103-
109+
if (translations.TryGetValue(defaultCulture, out var def))
110+
{
111+
return def.Biography ?? string.Empty;
112+
}
113+
104114
// 4. Default Neutral
105115
var defaultNeutral = System.Globalization.CultureInfo.GetCultureInfo(defaultCulture).TwoLetterISOLanguageName;
106-
if (translations.TryGetValue(defaultNeutral, out var defNeutral)) return defNeutral.Biography ?? string.Empty;
107-
116+
if (translations.TryGetValue(defaultNeutral, out var defNeutral))
117+
{
118+
return defNeutral.Biography ?? string.Empty;
119+
}
120+
108121
// 5. Fallback
109122
return string.Empty;
110123
}

0 commit comments

Comments
 (0)