Skip to content

Commit 31db00f

Browse files
Per Kopsdavidkallesen
authored andcommitted
fix(sample): critical DI, exception handling, and mapping fixes
1 parent f8e8850 commit 31db00f

File tree

7 files changed

+71
-25
lines changed

7 files changed

+71
-25
lines changed

sample/.editorconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,8 @@ dotnet_diagnostic.S1135.severity = suggestion # https://github.com/atc-net
511511
##########################################
512512
[*.{cs,csx,cake}]
513513

514+
MA0051.maximum_lines_per_method = 100
515+
514516
dotnet_diagnostic.CA1031.severity = none # https://learn.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/ca1031
515517
dotnet_diagnostic.CA1515.severity = none # Consider making public types internal https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1515
516518
dotnet_diagnostic.CA1848.severity = none # https://learn.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/ca1848

sample/src/Demo.Domain/Extensions/ApplicationBuilderExtensions.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ public static class ApplicationBuilderExtensions
55
public static IApplicationBuilder InitializeDatabase(this IApplicationBuilder app)
66
{
77
using var serviceScope = app.ApplicationServices.CreateScope();
8-
var serviceProvider = serviceScope.ServiceProvider;
9-
var options = serviceProvider.GetRequiredService<DbContextOptions<DemoDbContext>>();
10-
using var context = new DemoDbContext(options);
8+
var context = serviceScope.ServiceProvider.GetRequiredService<DemoDbContext>();
119

1210
if (context.Users.Any())
1311
{

sample/src/Demo.Domain/Extensions/ServiceCollectionExtensions.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@ public static IServiceCollection ConfigureDomainServices(
1717

1818
public static void DefineHandlersAndServices(this IServiceCollection services)
1919
{
20-
services.AddSingleton<IGetUsersHandler, GetUsersHandler>();
21-
services.AddSingleton<ICreateUserHandler, CreateUserHandler>();
22-
services.AddSingleton<IGetUserByIdHandler, GetUserByIdHandler>();
23-
services.AddSingleton<IGetUserByEmailHandler, GetUserByEmailHandler>();
24-
services.AddSingleton<IUpdateUserByIdHandler, UpdateUserByIdHandler>();
25-
services.AddSingleton<IDeleteUserByIdHandler, DeleteUserByIdHandler>();
20+
services.AddScoped<IGetUsersHandler, GetUsersHandler>();
21+
services.AddScoped<ICreateUserHandler, CreateUserHandler>();
22+
services.AddScoped<IGetUserByIdHandler, GetUserByIdHandler>();
23+
services.AddScoped<IGetUserByEmailHandler, GetUserByEmailHandler>();
24+
services.AddScoped<IUpdateUserByIdHandler, UpdateUserByIdHandler>();
25+
services.AddScoped<IDeleteUserByIdHandler, DeleteUserByIdHandler>();
2626
}
2727

2828
private static void SetupStorage(this IServiceCollection services)
2929
{
3030
// Add DbContext with In-Memory Database
31+
// Note: Using a fixed database name so data persists across requests
32+
// contextLifetime: Scoped for thread-safety, optionsLifetime: Singleton for test compatibility
3133
services.AddDbContext<DemoDbContext>(
32-
options => options.UseInMemoryDatabase(
33-
Guid.NewGuid().ToString()),
34-
ServiceLifetime.Singleton);
34+
options => options.UseInMemoryDatabase("DemoDatabase"),
35+
contextLifetime: ServiceLifetime.Scoped,
36+
optionsLifetime: ServiceLifetime.Singleton);
3537
}
3638
}

sample/src/Demo.Domain/MapsterConfig.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,29 @@ public static class MapsterConfig
44
{
55
public static void Register()
66
{
7+
// Map Country -> CountryEntity
8+
TypeAdapterConfig<Country, CountryEntity>
9+
.NewConfig();
10+
11+
// Map Address -> AddressEntity
12+
TypeAdapterConfig<Address, AddressEntity>
13+
.NewConfig();
14+
15+
// Map CreateUserRequest -> UserEntity with safe Uri parsing
716
TypeAdapterConfig<CreateUserRequest, UserEntity>
817
.NewConfig()
9-
.Map(dest => dest.HomePage, src => src.HomePage != null ? new Uri(src.HomePage) : null);
18+
.Map(dest => dest.HomePage, src => TryCreateUri(src.HomePage));
1019

1120
TypeAdapterConfig.GlobalSettings.Compile();
1221
}
22+
23+
private static Uri? TryCreateUri(string? url)
24+
{
25+
if (string.IsNullOrWhiteSpace(url))
26+
{
27+
return null;
28+
}
29+
30+
return Uri.TryCreate(url, UriKind.Absolute, out var uri) ? uri : null;
31+
}
1332
}

sample/src/Demo.Domain/Users/Handlers/CreateUserHandler.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@ public async Task<Results<CreatedAtRoute, BadRequest<string>, Conflict<string>>>
2222

2323
dbContext.Users.Add(userEntity);
2424

25-
var saveChangesResult = await dbContext.SaveChangesAsync(cancellationToken);
25+
try
26+
{
27+
var saveChangesResult = await dbContext.SaveChangesAsync(cancellationToken);
2628

27-
return saveChangesResult > 0
28-
? TypedResults.CreatedAtRoute(Api.Contracts.Names.UserDefinitionNames.GetUserById, new { userId })
29-
: TypedResults.BadRequest("Could not create user.");
29+
return saveChangesResult > 0
30+
? TypedResults.CreatedAtRoute(Api.Contracts.Names.UserDefinitionNames.GetUserById, new { userId })
31+
: TypedResults.BadRequest("Could not create user.");
32+
}
33+
catch (DbUpdateException)
34+
{
35+
return TypedResults.BadRequest("Could not create user due to a database error.");
36+
}
3037
}
3138
}

sample/src/Demo.Domain/Users/Handlers/DeleteUserByIdHandler.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,17 @@ public async Task<Results<NoContent, BadRequest<string>, NotFound>> ExecuteAsync
2525

2626
dbContext.Users.Remove(user);
2727

28-
var saveChangesResult = await dbContext.SaveChangesAsync(cancellationToken);
28+
try
29+
{
30+
var saveChangesResult = await dbContext.SaveChangesAsync(cancellationToken);
2931

30-
return saveChangesResult > 0
31-
? TypedResults.NoContent()
32-
: TypedResults.BadRequest($"Could not delete user with id '{parameters.UserId}'.");
32+
return saveChangesResult > 0
33+
? TypedResults.NoContent()
34+
: TypedResults.BadRequest($"Could not delete user with id '{parameters.UserId}'.");
35+
}
36+
catch (DbUpdateException)
37+
{
38+
return TypedResults.BadRequest($"Could not delete user with id '{parameters.UserId}' due to a database error.");
39+
}
3340
}
3441
}

sample/src/Demo.Domain/Users/Handlers/UpdateUserByIdHandler.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,22 @@ public async Task<Results<Ok<User>, BadRequest<string>, NotFound, Conflict<strin
6767
user.WorkAddress = MapAddress(parameters.Request.WorkAddress);
6868
}
6969

70-
var saveChangesResult = await dbContext.SaveChangesAsync(cancellationToken);
70+
try
71+
{
72+
var saveChangesResult = await dbContext.SaveChangesAsync(cancellationToken);
7173

72-
return saveChangesResult > 0
73-
? TypedResults.Ok(user.Adapt<User>())
74-
: TypedResults.BadRequest("Could not update user.");
74+
return saveChangesResult > 0
75+
? TypedResults.Ok(user.Adapt<User>())
76+
: TypedResults.BadRequest("Could not update user.");
77+
}
78+
catch (DbUpdateConcurrencyException)
79+
{
80+
return TypedResults.Conflict("The user was modified by another request. Please retry.");
81+
}
82+
catch (DbUpdateException)
83+
{
84+
return TypedResults.BadRequest("Could not update user due to a database error.");
85+
}
7586
}
7687

7788
private static bool IsModified(

0 commit comments

Comments
 (0)