- Updated column names from 'Timer' to 'RowVersion' in schema.sql for

- Introduced new BeerPost entity and updated BreweryPost and BreweryPostLocation entities to use 'RowVersion' for concurrency.
- Modified UserAccount, UserCredential, UserVerification, City, StateProvince, and Country entities to use 'RowVersion' instead of 'Timer'.
- Updated tests and command handlers to reflect changes in concurrency token from 'Timer' to 'RowVersion'.
- Adjusted BreweryDto and BreweryDtoMapper to include 'RowVersion' and updated mapping logic accordingly.
- Ensured all repository methods and interfaces are aligned with the new concurrency handling approach.
This commit is contained in:
Aaron Po
2026-08-19 16:15:34 -04:00
parent 81a76c9baf
commit ab265de54d
19 changed files with 290 additions and 184 deletions

View File

@@ -1,51 +1,35 @@
namespace Features.Breweries.Dtos;
/// <summary>Location details of a brewery, as returned by the API.</summary>
public class BreweryLocationDto
{
public Guid BreweryPostLocationId { get; set; }
public Guid CityId { get; set; }
public string CityName { get; set; } = string.Empty;
/// <param name="Coordinates">The brewery's geographic coordinates, in a raw binary representation.</param>
public record BreweryLocationDto(
Guid BreweryPostLocationId,
Guid CityId,
string CityName,
string StateProvinceName,
string StateProvinceCode,
string CountryName,
string CountryCode,
string AddressLine1,
string? AddressLine2,
string PostalCode,
byte[]? Coordinates
);
public string StateProvinceName { get; set; } = string.Empty;
public string StateProvinceCode { get; set; } = string.Empty;
public string CountryName { get; set; } = string.Empty;
public string CountryCode { get; set; } = string.Empty;
public string AddressLine1 { get; set; } = string.Empty;
public string? AddressLine2 { get; set; }
public string PostalCode { get; set; } = string.Empty;
/// <summary>Gets or sets the brewery's geographic coordinates, in a raw binary representation.</summary>
public byte[]? Coordinates { get; set; }
}
public record BeerPreviewDto(Guid BeerPostId, string Name, decimal ABV, int IBU);
/// <summary>A brewery post, as returned by the API.</summary>
public class BreweryDto
{
public Guid BreweryPostId { get; set; }
public Guid PostedById { get; set; }
public string BreweryName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
/// <summary>Gets or sets the date and time of the last edit, or <see langword="null"/> if never edited.</summary>
public DateTime? UpdatedAt { get; set; }
/// <summary>Gets or sets the row-version/concurrency token used to detect conflicting concurrent updates.</summary>
public byte[]? Timer { get; set; }
/// <summary>Gets or sets the brewery's location, or <see langword="null"/> if none has been set.</summary>
public BreweryLocationDto? Location { get; set; }
}
/// <param name="UpdatedAt">The date and time of the last edit, or <see langword="null"/> if never edited.</param>
/// <param name="RowVersion">The row-version/concurrency token used to detect conflicting concurrent updates.</param>
/// <param name="Location">The brewery's location, or <see langword="null"/> if none has been set.</param>
public record BreweryDto(
Guid BreweryPostId,
Guid PostedById,
string BreweryName,
string Description,
DateTime CreatedAt,
DateTime? UpdatedAt,
byte[]? RowVersion,
BreweryLocationDto? Location,
List<BeerPreviewDto> BeerPosts
);

View File

@@ -11,31 +11,42 @@ public static class BreweryDtoMapper
/// </summary>
public static BreweryDto ToDto(this BreweryPost brewery)
{
return new BreweryDto
{
BreweryPostId = brewery.BreweryPostId,
PostedById = brewery.PostedById,
BreweryName = brewery.BreweryName,
Description = brewery.Description,
CreatedAt = brewery.CreatedAt,
UpdatedAt = brewery.UpdatedAt,
Timer = brewery.Timer,
Location = brewery.Location is null
return new BreweryDto(
BreweryPostId: brewery.BreweryPostId,
PostedById: brewery.PostedById,
BreweryName: brewery.BreweryName,
Description: brewery.Description,
CreatedAt: brewery.CreatedAt,
UpdatedAt: brewery.UpdatedAt,
RowVersion: brewery.RowVersion,
Location: brewery.Location is null
? null
: new BreweryLocationDto
{
BreweryPostLocationId = brewery.Location.BreweryPostLocationId,
CityId = brewery.Location.CityId,
CityName = brewery.Location.City?.CityName ?? string.Empty,
StateProvinceName = brewery.Location.City?.StateProvince?.StateProvinceName ?? string.Empty,
StateProvinceCode = brewery.Location.City?.StateProvince?.ISO3166_2 ?? string.Empty,
CountryName = brewery.Location.City?.StateProvince?.Country?.CountryName ?? string.Empty,
CountryCode = brewery.Location.City?.StateProvince?.Country?.ISO3166_1 ?? string.Empty,
AddressLine1 = brewery.Location.AddressLine1,
AddressLine2 = brewery.Location.AddressLine2,
PostalCode = brewery.Location.PostalCode,
Coordinates = brewery.Location.Coordinates,
},
};
: new BreweryLocationDto(
BreweryPostLocationId: brewery.Location.BreweryPostLocationId,
CityId: brewery.Location.CityId,
CityName: brewery.Location.City?.CityName ?? string.Empty,
StateProvinceName: brewery.Location.City?.StateProvince?.StateProvinceName
?? string.Empty,
StateProvinceCode: brewery.Location.City?.StateProvince?.ISO3166_2
?? string.Empty,
CountryName: brewery.Location.City?.StateProvince?.Country?.CountryName
?? string.Empty,
CountryCode: brewery.Location.City?.StateProvince?.Country?.ISO3166_1
?? string.Empty,
AddressLine1: brewery.Location.AddressLine1,
AddressLine2: brewery.Location.AddressLine2,
PostalCode: brewery.Location.PostalCode,
Coordinates: brewery.Location.Coordinates
),
BeerPosts:
[
.. brewery.BeerPosts.Select(beer => new BeerPreviewDto(
BeerPostId: beer.BeerPostId,
Name: beer.Name,
ABV: beer.ABV,
IBU: beer.IBU
)),
]
);
}
}