Skip to content

Commit d9101db

Browse files
authored
Merge pull request #205 from nanotaboada/feature/c-sharp-12-primary-constructors
feature/c-sharp-12-primary-constructors
2 parents 19eb114 + b6bf387 commit d9101db

File tree

3 files changed

+90
-88
lines changed

3 files changed

+90
-88
lines changed

src/Dotnet.Samples.AspNetCore.WebApi/Controllers/PlayerController.cs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ public class PlayerController(
1515
IValidator<PlayerRequestModel> validator
1616
) : ControllerBase
1717
{
18-
private readonly IPlayerService _playerService = playerService;
19-
private readonly ILogger<PlayerController> _logger = logger;
20-
private readonly IValidator<PlayerRequestModel> validator = validator;
21-
2218
/* -------------------------------------------------------------------------
2319
* HTTP POST
2420
* ---------------------------------------------------------------------- */
@@ -45,17 +41,22 @@ public async Task<IResult> PostAsync([FromBody] PlayerRequestModel player)
4541
.Errors.Select(error => new { error.PropertyName, error.ErrorMessage })
4642
.ToArray();
4743

48-
_logger.LogWarning("POST validation failed: {@Errors}", errors);
44+
logger.LogWarning("POST /players validation failed: {@Errors}", errors);
4945
return TypedResults.BadRequest(errors);
5046
}
5147

52-
if (await _playerService.RetrieveByIdAsync(player.Id) != null)
48+
if (await playerService.RetrieveByIdAsync(player.Id) != null)
5349
{
50+
logger.LogWarning(
51+
"POST /players failed: Player with ID {Id} already exists",
52+
player.Id
53+
);
5454
return TypedResults.Conflict();
5555
}
5656

57-
var result = await _playerService.CreateAsync(player);
57+
var result = await playerService.CreateAsync(player);
5858

59+
logger.LogInformation("POST /players created: {@Player}", result);
5960
return TypedResults.CreatedAtRoute(
6061
routeName: "GetById",
6162
routeValues: new { id = result.Id },
@@ -77,14 +78,16 @@ public async Task<IResult> PostAsync([FromBody] PlayerRequestModel player)
7778
[ProducesResponseType(StatusCodes.Status404NotFound)]
7879
public async Task<IResult> GetAsync()
7980
{
80-
var players = await _playerService.RetrieveAsync();
81+
var players = await playerService.RetrieveAsync();
8182

8283
if (players.Count > 0)
8384
{
85+
logger.LogInformation("GET /players retrieved");
8486
return TypedResults.Ok(players);
8587
}
8688
else
8789
{
90+
logger.LogWarning("GET /players not found");
8891
return TypedResults.NotFound();
8992
}
9093
}
@@ -100,14 +103,15 @@ public async Task<IResult> GetAsync()
100103
[ProducesResponseType(StatusCodes.Status404NotFound)]
101104
public async Task<IResult> GetByIdAsync([FromRoute] long id)
102105
{
103-
var player = await _playerService.RetrieveByIdAsync(id);
104-
106+
var player = await playerService.RetrieveByIdAsync(id);
105107
if (player != null)
106108
{
109+
logger.LogInformation("GET /players/{Id} retrieved: {@Player}", id, player);
107110
return TypedResults.Ok(player);
108111
}
109112
else
110113
{
114+
logger.LogWarning("GET /players/{Id} not found", id);
111115
return TypedResults.NotFound();
112116
}
113117
}
@@ -123,14 +127,19 @@ public async Task<IResult> GetByIdAsync([FromRoute] long id)
123127
[ProducesResponseType(StatusCodes.Status404NotFound)]
124128
public async Task<IResult> GetBySquadNumberAsync([FromRoute] int squadNumber)
125129
{
126-
var player = await _playerService.RetrieveBySquadNumberAsync(squadNumber);
127-
130+
var player = await playerService.RetrieveBySquadNumberAsync(squadNumber);
128131
if (player != null)
129132
{
133+
logger.LogInformation(
134+
"GET /players/squad/{SquadNumber} retrieved: {@Player}",
135+
squadNumber,
136+
player
137+
);
130138
return TypedResults.Ok(player);
131139
}
132140
else
133141
{
142+
logger.LogWarning("GET /players/squad/{SquadNumber} not found", squadNumber);
134143
return TypedResults.NotFound();
135144
}
136145
}
@@ -155,24 +164,22 @@ public async Task<IResult> GetBySquadNumberAsync([FromRoute] int squadNumber)
155164
public async Task<IResult> PutAsync([FromRoute] long id, [FromBody] PlayerRequestModel player)
156165
{
157166
var validation = await validator.ValidateAsync(player);
158-
159167
if (!validation.IsValid)
160168
{
161169
var errors = validation
162170
.Errors.Select(error => new { error.PropertyName, error.ErrorMessage })
163171
.ToArray();
164172

165-
_logger.LogWarning("PUT /players/{Id} validation failed: {@Errors}", id, errors);
173+
logger.LogWarning("PUT /players/{Id} validation failed: {@Errors}", id, errors);
166174
return TypedResults.BadRequest(errors);
167175
}
168-
169-
if (await _playerService.RetrieveByIdAsync(id) == null)
176+
if (await playerService.RetrieveByIdAsync(id) == null)
170177
{
178+
logger.LogWarning("PUT /players/{Id} not found", id);
171179
return TypedResults.NotFound();
172180
}
173-
174-
await _playerService.UpdateAsync(player);
175-
181+
await playerService.UpdateAsync(player);
182+
logger.LogInformation("PUT /players/{Id} updated: {@Player}", id, player);
176183
return TypedResults.NoContent();
177184
}
178185

@@ -191,14 +198,15 @@ public async Task<IResult> PutAsync([FromRoute] long id, [FromBody] PlayerReques
191198
[ProducesResponseType(StatusCodes.Status404NotFound)]
192199
public async Task<IResult> DeleteAsync([FromRoute] long id)
193200
{
194-
if (await _playerService.RetrieveByIdAsync(id) == null)
201+
if (await playerService.RetrieveByIdAsync(id) == null)
195202
{
203+
logger.LogWarning("DELETE /players/{Id} not found", id);
196204
return TypedResults.NotFound();
197205
}
198206
else
199207
{
200-
await _playerService.DeleteAsync(id);
201-
208+
await playerService.DeleteAsync(id);
209+
logger.LogInformation("DELETE /players/{Id} deleted", id);
202210
return TypedResults.NoContent();
203211
}
204212
}

src/Dotnet.Samples.AspNetCore.WebApi/Data/Repository.cs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,15 @@
22

33
namespace Dotnet.Samples.AspNetCore.WebApi.Data;
44

5-
public class Repository<T> : IRepository<T>
5+
public class Repository<T>(DbContext dbContext) : IRepository<T>
66
where T : class
77
{
8-
protected readonly DbContext _dbContext;
9-
protected readonly DbSet<T> _dbSet;
10-
11-
public Repository(DbContext dbContext)
12-
{
13-
_dbContext = dbContext;
14-
_dbSet = _dbContext.Set<T>();
15-
}
8+
protected readonly DbSet<T> _dbSet = dbContext.Set<T>();
169

1710
public async Task AddAsync(T entity)
1811
{
1912
await _dbSet.AddAsync(entity);
20-
await _dbContext.SaveChangesAsync();
13+
await dbContext.SaveChangesAsync();
2114
}
2215

2316
public async Task<List<T>> GetAllAsync() => await _dbSet.AsNoTracking().ToListAsync();
@@ -27,7 +20,7 @@ public async Task AddAsync(T entity)
2720
public async Task UpdateAsync(T entity)
2821
{
2922
_dbSet.Update(entity);
30-
await _dbContext.SaveChangesAsync();
23+
await dbContext.SaveChangesAsync();
3124
}
3225

3326
public async Task RemoveAsync(long id)
@@ -36,7 +29,7 @@ public async Task RemoveAsync(long id)
3629
if (entity != null)
3730
{
3831
_dbSet.Remove(entity);
39-
await _dbContext.SaveChangesAsync();
32+
await dbContext.SaveChangesAsync();
4033
}
4134
}
4235
}

src/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,47 @@ public class PlayerService(
1212
IMapper mapper
1313
) : IPlayerService
1414
{
15+
/// <summary>
16+
/// Creates a MemoryCacheEntryOptions instance with Normal priority,
17+
/// SlidingExpiration of 10 minutes and AbsoluteExpiration of 1 hour.
18+
/// </summary>
19+
private static readonly MemoryCacheEntryOptions CacheEntryOptions =
20+
new MemoryCacheEntryOptions()
21+
.SetPriority(CacheItemPriority.Normal)
22+
.SetSlidingExpiration(TimeSpan.FromMinutes(10))
23+
.SetAbsoluteExpiration(TimeSpan.FromHours(1));
24+
25+
/// <summary>
26+
/// The key used to store the list of Players in the cache.
27+
/// </summary>
1528
private static readonly string CacheKey_RetrieveAsync = nameof(RetrieveAsync);
29+
30+
/// <summary>
31+
/// The key used to store the environment variable for ASP.NET Core.
32+
/// <br/>
33+
/// <see href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0">
34+
/// Use multiple environments in ASP.NET Core
35+
/// </see>
36+
/// </summary>
1637
private static readonly string AspNetCore_Environment = "ASPNETCORE_ENVIRONMENT";
17-
private static readonly string Development = "Development";
1838

19-
private readonly IPlayerRepository _playerRepository = playerRepository;
20-
private readonly ILogger<PlayerService> _logger = logger;
21-
private readonly IMemoryCache _memoryCache = memoryCache;
22-
private readonly IMapper _mapper = mapper;
39+
/// <summary>
40+
/// The value used to check if the environment is Development.
41+
/// </summary>
42+
private static readonly string Development = "Development";
2343

2444
/* -------------------------------------------------------------------------
2545
* Create
2646
* ---------------------------------------------------------------------- */
2747

2848
public async Task<PlayerResponseModel> CreateAsync(PlayerRequestModel playerRequestModel)
2949
{
30-
var player = _mapper.Map<Player>(playerRequestModel);
31-
await _playerRepository.AddAsync(player);
32-
_logger.LogInformation("Player added to Repository: {Player}", player);
33-
_memoryCache.Remove(CacheKey_RetrieveAsync);
34-
_logger.LogInformation(
35-
"Removed objects from Cache with Key: {Key}",
36-
CacheKey_RetrieveAsync
37-
);
38-
return _mapper.Map<PlayerResponseModel>(player);
50+
var player = mapper.Map<Player>(playerRequestModel);
51+
await playerRepository.AddAsync(player);
52+
logger.LogInformation("Player added to Repository: {Player}", player);
53+
memoryCache.Remove(CacheKey_RetrieveAsync);
54+
logger.LogInformation("Removed objects from Cache with Key: {Key}", CacheKey_RetrieveAsync);
55+
return mapper.Map<PlayerResponseModel>(player);
3956
}
4057

4158
/* -------------------------------------------------------------------------
@@ -44,48 +61,45 @@ public async Task<PlayerResponseModel> CreateAsync(PlayerRequestModel playerRequ
4461

4562
public async Task<List<PlayerResponseModel>> RetrieveAsync()
4663
{
47-
if (_memoryCache.TryGetValue(CacheKey_RetrieveAsync, out List<PlayerResponseModel>? cached))
64+
if (memoryCache.TryGetValue(CacheKey_RetrieveAsync, out List<PlayerResponseModel>? cached))
4865
{
49-
_logger.LogInformation("Players retrieved from Cache");
66+
logger.LogInformation("Players retrieved from Cache");
5067
return cached!;
5168
}
5269
else
5370
{
54-
// Use multiple environments in ASP.NET Core
55-
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0
5671
if (Environment.GetEnvironmentVariable(AspNetCore_Environment) == Development)
5772
{
5873
await SimulateRepositoryDelayAsync();
5974
}
60-
61-
var players = await _playerRepository.GetAllAsync();
62-
_logger.LogInformation("Players retrieved from Repository");
63-
var playerResponseModels = _mapper.Map<List<PlayerResponseModel>>(players);
64-
using (var cacheEntry = _memoryCache.CreateEntry(CacheKey_RetrieveAsync))
75+
var players = await playerRepository.GetAllAsync();
76+
logger.LogInformation("Players retrieved from Repository");
77+
var playerResponseModels = mapper.Map<List<PlayerResponseModel>>(players);
78+
using (var cacheEntry = memoryCache.CreateEntry(CacheKey_RetrieveAsync))
6579
{
66-
_logger.LogInformation(
80+
logger.LogInformation(
6781
"{Count} entries created in Cache with key: {Key}",
6882
playerResponseModels.Count,
6983
CacheKey_RetrieveAsync
7084
);
7185
cacheEntry.SetSize(playerResponseModels.Count);
7286
cacheEntry.Value = playerResponseModels;
73-
cacheEntry.SetOptions(GetMemoryCacheEntryOptions());
87+
cacheEntry.SetOptions(CacheEntryOptions);
7488
}
7589
return playerResponseModels;
7690
}
7791
}
7892

7993
public async Task<PlayerResponseModel?> RetrieveByIdAsync(long id)
8094
{
81-
var player = await _playerRepository.FindByIdAsync(id);
82-
return player is not null ? _mapper.Map<PlayerResponseModel>(player) : null;
95+
var player = await playerRepository.FindByIdAsync(id);
96+
return player is not null ? mapper.Map<PlayerResponseModel>(player) : null;
8397
}
8498

8599
public async Task<PlayerResponseModel?> RetrieveBySquadNumberAsync(int squadNumber)
86100
{
87-
var player = await _playerRepository.FindBySquadNumberAsync(squadNumber);
88-
return player is not null ? _mapper.Map<PlayerResponseModel>(player) : null;
101+
var player = await playerRepository.FindBySquadNumberAsync(squadNumber);
102+
return player is not null ? mapper.Map<PlayerResponseModel>(player) : null;
89103
}
90104

91105
/* -------------------------------------------------------------------------
@@ -94,13 +108,13 @@ public async Task<List<PlayerResponseModel>> RetrieveAsync()
94108

95109
public async Task UpdateAsync(PlayerRequestModel playerRequestModel)
96110
{
97-
if (await _playerRepository.FindByIdAsync(playerRequestModel.Id) is Player player)
111+
if (await playerRepository.FindByIdAsync(playerRequestModel.Id) is Player player)
98112
{
99-
_mapper.Map(playerRequestModel, player);
100-
await _playerRepository.UpdateAsync(player);
101-
_logger.LogInformation("Player updated in Repository: {Player}", player);
102-
_memoryCache.Remove(CacheKey_RetrieveAsync);
103-
_logger.LogInformation(
113+
mapper.Map(playerRequestModel, player);
114+
await playerRepository.UpdateAsync(player);
115+
logger.LogInformation("Player updated in Repository: {Player}", player);
116+
memoryCache.Remove(CacheKey_RetrieveAsync);
117+
logger.LogInformation(
104118
"Removed objects from Cache with Key: {Key}",
105119
CacheKey_RetrieveAsync
106120
);
@@ -113,31 +127,18 @@ public async Task UpdateAsync(PlayerRequestModel playerRequestModel)
113127

114128
public async Task DeleteAsync(long id)
115129
{
116-
if (await _playerRepository.FindByIdAsync(id) is not null)
130+
if (await playerRepository.FindByIdAsync(id) is not null)
117131
{
118-
await _playerRepository.RemoveAsync(id);
119-
_logger.LogInformation("Player with Id {Id} removed from Repository", id);
120-
_memoryCache.Remove(CacheKey_RetrieveAsync);
121-
_logger.LogInformation(
132+
await playerRepository.RemoveAsync(id);
133+
logger.LogInformation("Player with Id {Id} removed from Repository", id);
134+
memoryCache.Remove(CacheKey_RetrieveAsync);
135+
logger.LogInformation(
122136
"Removed objects from Cache with Key: {Key}",
123137
CacheKey_RetrieveAsync
124138
);
125139
}
126140
}
127141

128-
/// <summary>
129-
/// Creates a MemoryCacheEntryOptions instance with Normal priority,
130-
/// SlidingExpiration of 10 minutes and AbsoluteExpiration of 1 hour.
131-
/// </summary>
132-
/// <returns>A MemoryCacheEntryOptions instance with the specified options.</returns>
133-
private static MemoryCacheEntryOptions GetMemoryCacheEntryOptions()
134-
{
135-
return new MemoryCacheEntryOptions()
136-
.SetPriority(CacheItemPriority.Normal)
137-
.SetSlidingExpiration(TimeSpan.FromMinutes(10))
138-
.SetAbsoluteExpiration(TimeSpan.FromHours(1));
139-
}
140-
141142
/// <summary>
142143
/// Simulates a delay in the repository call to mimic a long-running operation.
143144
/// This is only used in the Development environment to simulate a delay
@@ -147,7 +148,7 @@ private static MemoryCacheEntryOptions GetMemoryCacheEntryOptions()
147148
private async Task SimulateRepositoryDelayAsync()
148149
{
149150
var milliseconds = new Random().Next(2600, 4200);
150-
_logger.LogInformation(
151+
logger.LogInformation(
151152
"Simulating a random delay of {Milliseconds} milliseconds...",
152153
milliseconds
153154
);

0 commit comments

Comments
 (0)