This commit is contained in:
Aaron Po
2026-08-19 16:27:55 -04:00
parent ab265de54d
commit 2b2df0053f

View File

@@ -23,27 +23,27 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
IBreweryRepository IBreweryRepository
{ {
private const string SelectColumns = """ private const string SelectColumns = """
bp.BreweryPostID, bp.BreweryPostID,
bp.PostedByID, bp.PostedByID,
bp.BreweryName, bp.BreweryName,
bp.Description, bp.Description,
bp.CreatedAt, bp.CreatedAt,
bp.UpdatedAt, bp.UpdatedAt,
bp.RowVersion, bp.RowVersion,
bpl.BreweryPostLocationID, bpl.BreweryPostLocationID,
bpl.CityID, bpl.CityID,
bpl.AddressLine1, bpl.AddressLine1,
bpl.AddressLine2, bpl.AddressLine2,
bpl.PostalCode, bpl.PostalCode,
bpl.Coordinates, bpl.Coordinates,
c.CityName, c.CityName,
c.StateProvinceID, c.StateProvinceID,
sp.StateProvinceName, sp.StateProvinceName,
sp.ISO3166_2, sp.ISO3166_2,
sp.CountryID, sp.CountryID,
co.CountryName, co.CountryName,
co.ISO3166_1 co.ISO3166_1
"""; """;
/// <summary> /// <summary>
/// Joins from <c>dbo.BreweryPost</c> down to <c>dbo.Country</c> so a row carries everything needed /// Joins from <c>dbo.BreweryPost</c> down to <c>dbo.Country</c> so a row carries everything needed
@@ -52,12 +52,12 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
/// behavior), since a brewery post may have no location. /// behavior), since a brewery post may have no location.
/// </summary> /// </summary>
private const string FromJoins = """ private const string FromJoins = """
dbo.BreweryPost bp dbo.BreweryPost bp
LEFT JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID LEFT JOIN dbo.BreweryPostLocation bpl ON bp.BreweryPostID = bpl.BreweryPostID
LEFT JOIN dbo.City c ON bpl.CityID = c.CityID LEFT JOIN dbo.City c ON bpl.CityID = c.CityID
LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID LEFT JOIN dbo.StateProvince sp ON c.StateProvinceID = sp.StateProvinceID
LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID LEFT JOIN dbo.Country co ON sp.CountryID = co.CountryID
"""; """;
/// <inheritdoc/> /// <inheritdoc/>
public async Task<BreweryPost?> GetByIdAsync(Guid id) public async Task<BreweryPost?> GetByIdAsync(Guid id)
@@ -65,10 +65,10 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
await using DbConnection connection = await CreateConnection(); await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand(); await using DbCommand command = connection.CreateCommand();
command.CommandText = $""" command.CommandText = $"""
SELECT {SelectColumns} SELECT {SelectColumns}
FROM {FromJoins} FROM {FromJoins}
WHERE bp.BreweryPostID = @BreweryPostId WHERE bp.BreweryPostID = @BreweryPostId
"""; """;
AddParameter(command, "@BreweryPostId", id); AddParameter(command, "@BreweryPostId", id);
await using DbDataReader reader = await command.ExecuteReaderAsync(); await using DbDataReader reader = await command.ExecuteReaderAsync();
@@ -76,16 +76,17 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<IEnumerable<BreweryPost>> GetAllAsync(int? limit, int? offset) public async Task<IEnumerable<BreweryPost>> GetAllAsync(int? limit,
int? offset)
{ {
await using DbConnection connection = await CreateConnection(); await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand(); await using DbCommand command = connection.CreateCommand();
command.CommandText = $""" command.CommandText = $"""
SELECT {SelectColumns} SELECT {SelectColumns}
FROM {FromJoins} FROM {FromJoins}
ORDER BY bp.CreatedAt DESC ORDER BY bp.CreatedAt DESC
OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY
"""; """;
AddParameter(command, "@Offset", offset ?? 0); AddParameter(command, "@Offset", offset ?? 0);
AddParameter(command, "@Limit", limit ?? int.MaxValue); AddParameter(command, "@Limit", limit ?? int.MaxValue);
@@ -102,19 +103,25 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
public async Task<BreweryPost> UpdateAsync(BreweryPost brewery) public async Task<BreweryPost> UpdateAsync(BreweryPost brewery)
{ {
await using DbConnection connection = await CreateConnection(); await using DbConnection connection = await CreateConnection();
await using DbTransaction transaction = await connection.BeginTransactionAsync(); await using DbTransaction transaction =
await connection.BeginTransactionAsync();
try try
{ {
bool breweryExists = bool breweryExists =
await connection.ExecuteScalarAsync<int?>( await connection.ExecuteScalarAsync<int?>(
new CommandDefinition( new CommandDefinition(
"SELECT 1 FROM dbo.BreweryPost WHERE BreweryPostID = @BreweryPostId", """
new { brewery.BreweryPostId }, SELECT 1 FROM
transaction dbo.BreweryPost
WHERE
BreweryPostID = @BreweryPostId
""",
new { brewery.BreweryPostId },
transaction
)
) )
) is not null;
is not null;
if (!breweryExists) if (!breweryExists)
throw new NotFoundException("Brewery not found."); throw new NotFoundException("Brewery not found.");
@@ -123,13 +130,13 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
{ {
bool cityExists = bool cityExists =
await connection.ExecuteScalarAsync<int?>( await connection.ExecuteScalarAsync<int?>(
new CommandDefinition( new CommandDefinition(
"SELECT 1 FROM dbo.City WHERE CityID = @CityId", "SELECT 1 FROM dbo.City WHERE CityID = @CityId",
new { brewery.Location.CityId }, new { brewery.Location.CityId },
transaction transaction
)
) )
) is not null;
is not null;
if (!cityExists) if (!cityExists)
throw new NotFoundException("City not found."); throw new NotFoundException("City not found.");
@@ -139,8 +146,12 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition( new CommandDefinition(
""" """
UPDATE dbo.BreweryPost UPDATE dbo.BreweryPost
SET BreweryName = @BreweryName, Description = @Description, UpdatedAt = GETDATE() SET BreweryName = @BreweryName,
WHERE BreweryPostID = @BreweryPostId AND RowVersion = @RowVersion Description = @Description,
UpdatedAt = GETDATE()
WHERE
BreweryPostID = @BreweryPostId
AND RowVersion = @RowVersion
""", """,
new new
{ {
@@ -163,7 +174,11 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
// No location supplied: clear any existing location for this brewery. // No location supplied: clear any existing location for this brewery.
await connection.ExecuteAsync( await connection.ExecuteAsync(
new CommandDefinition( new CommandDefinition(
"DELETE FROM dbo.BreweryPostLocation WHERE BreweryPostID = @BreweryPostId", """
DELETE FROM
dbo.BreweryPostLocation
WHERE BreweryPostID = @BreweryPostId
""",
new { brewery.BreweryPostId }, new { brewery.BreweryPostId },
transaction transaction
) )
@@ -173,21 +188,28 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
{ {
bool locationExists = bool locationExists =
await connection.ExecuteScalarAsync<int?>( await connection.ExecuteScalarAsync<int?>(
new CommandDefinition( new CommandDefinition(
"SELECT 1 FROM dbo.BreweryPostLocation WHERE BreweryPostID = @BreweryPostId", """
new { brewery.BreweryPostId }, SELECT 1
transaction FROM dbo.BreweryPostLocation
WHERE BreweryPostID = @BreweryPostId
""",
new { brewery.BreweryPostId },
transaction
)
) )
) is not null;
is not null;
if (locationExists) if (locationExists)
await connection.ExecuteAsync( await connection.ExecuteAsync(
new CommandDefinition( new CommandDefinition(
""" """
UPDATE dbo.BreweryPostLocation UPDATE dbo.BreweryPostLocation
SET CityID = @CityId, AddressLine1 = @AddressLine1, AddressLine2 = @AddressLine2, SET CityID = @CityId,
PostalCode = @PostalCode, Coordinates = @Coordinates AddressLine1 = @AddressLine1,
AddressLine2 = @AddressLine2,
PostalCode = @PostalCode,
Coordinates = @Coordinates
WHERE BreweryPostID = @BreweryPostId WHERE BreweryPostID = @BreweryPostId
""", """,
new new
@@ -247,9 +269,9 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
} }
return await GetByIdAsync(brewery.BreweryPostId) return await GetByIdAsync(brewery.BreweryPostId)
?? throw new InvalidOperationException( ?? throw new InvalidOperationException(
$"Brewery '{brewery.BreweryPostId}' was not found after a successful update." $"Brewery '{brewery.BreweryPostId}' was not found after a successful update."
); );
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -257,7 +279,12 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
{ {
await using DbConnection connection = await CreateConnection(); await using DbConnection connection = await CreateConnection();
int rows = await connection.ExecuteAsync( int rows = await connection.ExecuteAsync(
"DELETE FROM dbo.BreweryPost WHERE BreweryPostID = @BreweryPostId", """
DELETE FROM
dbo.BreweryPost
WHERE
BreweryPostID = @BreweryPostId
""",
new { BreweryPostId = id } new { BreweryPostId = id }
); );
@@ -269,35 +296,49 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
public async Task CreateAsync(BreweryPost brewery) public async Task CreateAsync(BreweryPost brewery)
{ {
if (brewery.Location is null) if (brewery.Location is null)
throw new ArgumentException("Location must be provided when creating a brewery."); throw new ArgumentException(
"Location must be provided when creating a brewery.");
await using DbConnection connection = await CreateConnection(); await using DbConnection connection = await CreateConnection();
await using DbTransaction transaction = await connection.BeginTransactionAsync(); await using DbTransaction transaction =
await connection.BeginTransactionAsync();
try try
{ {
bool userExists = bool userExists =
await connection.ExecuteScalarAsync<int?>( await connection.ExecuteScalarAsync<int?>(
new CommandDefinition( new CommandDefinition(
"SELECT 1 FROM dbo.UserAccount WHERE UserAccountID = @PostedById", """
new { brewery.PostedById }, SELECT 1
transaction FROM
dbo.UserAccount
WHERE
UserAccountID = @PostedById
""",
new { brewery.PostedById },
transaction
)
) )
) is not null;
is not null;
if (!userExists) if (!userExists)
throw new NotFoundException("User not found."); throw new NotFoundException("User not found.");
bool cityExists = bool cityExists =
await connection.ExecuteScalarAsync<int?>( await connection.ExecuteScalarAsync<int?>(
new CommandDefinition( new CommandDefinition(
"SELECT 1 FROM dbo.City WHERE CityID = @CityId", """
new { brewery.Location.CityId }, SELECT 1
transaction FROM
dbo.City
WHERE
CityID = @CityId
""",
new { brewery.Location.CityId },
transaction
)
) )
) is not null;
is not null;
if (!cityExists) if (!cityExists)
throw new NotFoundException("City not found."); throw new NotFoundException("City not found.");
@@ -306,15 +347,9 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition( new CommandDefinition(
""" """
INSERT INTO dbo.BreweryPost INSERT INTO dbo.BreweryPost
(BreweryPostID, (BreweryPostID, BreweryName, Description, PostedByID)
BreweryName,
Description,
PostedByID)
VALUES VALUES
(@BreweryPostId, (@BreweryPostId, @BreweryName, @Description, @PostedById)
@BreweryName,
@Description,
@PostedById)
""", """,
new new
{ {
@@ -331,8 +366,20 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
new CommandDefinition( new CommandDefinition(
""" """
INSERT INTO dbo.BreweryPostLocation INSERT INTO dbo.BreweryPostLocation
(BreweryPostLocationID, BreweryPostID, CityID, AddressLine1, AddressLine2, PostalCode, Coordinates) (BreweryPostLocationID,
VALUES (@BreweryPostLocationId, @BreweryPostId, @CityId, @AddressLine1, @AddressLine2, @PostalCode, @Coordinates) BreweryPostID,
CityID,
AddressLine1,
AddressLine2,
PostalCode,
Coordinates)
VALUES (@BreweryPostLocationId,
@BreweryPostId,
@CityId,
@AddressLine1,
@AddressLine2,
@PostalCode,
@Coordinates)
""", """,
new new
{ {
@@ -378,7 +425,9 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
BreweryName = reader.GetString(ordBreweryName), BreweryName = reader.GetString(ordBreweryName),
Description = reader.GetString(ordDescription), Description = reader.GetString(ordDescription),
CreatedAt = reader.GetDateTime(ordCreatedAt), CreatedAt = reader.GetDateTime(ordCreatedAt),
UpdatedAt = reader.IsDBNull(ordUpdatedAt) ? null : reader.GetDateTime(ordUpdatedAt), UpdatedAt = reader.IsDBNull(ordUpdatedAt)
? null
: reader.GetDateTime(ordUpdatedAt),
RowVersion = reader.IsDBNull(ordRowVersion) RowVersion = reader.IsDBNull(ordRowVersion)
? null ? null
: reader.GetFieldValue<byte[]>(ordRowVersion), : reader.GetFieldValue<byte[]>(ordRowVersion),
@@ -396,7 +445,8 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
BreweryPostId = brewery.BreweryPostId, BreweryPostId = brewery.BreweryPostId,
CityId = reader.GetGuid(reader.GetOrdinal("CityId")), CityId = reader.GetGuid(reader.GetOrdinal("CityId")),
City = MapCity(reader), City = MapCity(reader),
AddressLine1 = reader.GetString(reader.GetOrdinal("AddressLine1")), AddressLine1 =
reader.GetString(reader.GetOrdinal("AddressLine1")),
AddressLine2 = reader.IsDBNull(ordAddressLine2) AddressLine2 = reader.IsDBNull(ordAddressLine2)
? null ? null
: reader.GetString(ordAddressLine2), : reader.GetString(ordAddressLine2),
@@ -421,24 +471,30 @@ public class BreweryRepository(ISqlConnectionFactory connectionFactory)
{ {
CityId = reader.GetGuid(reader.GetOrdinal("CityId")), CityId = reader.GetGuid(reader.GetOrdinal("CityId")),
CityName = reader.GetString(reader.GetOrdinal("CityName")), CityName = reader.GetString(reader.GetOrdinal("CityName")),
StateProvinceId = reader.GetGuid(reader.GetOrdinal("StateProvinceId")), StateProvinceId =
reader.GetGuid(reader.GetOrdinal("StateProvinceId")),
StateProvince = new StateProvince StateProvince = new StateProvince
{ {
StateProvinceId = reader.GetGuid(reader.GetOrdinal("StateProvinceId")), StateProvinceId =
StateProvinceName = reader.GetString(reader.GetOrdinal("StateProvinceName")), reader.GetGuid(reader.GetOrdinal("StateProvinceId")),
StateProvinceName =
reader.GetString(reader.GetOrdinal("StateProvinceName")),
ISO3166_2 = reader.GetString(reader.GetOrdinal("ISO3166_2")), ISO3166_2 = reader.GetString(reader.GetOrdinal("ISO3166_2")),
CountryId = reader.GetGuid(reader.GetOrdinal("CountryId")), CountryId = reader.GetGuid(reader.GetOrdinal("CountryId")),
Country = new Country Country = new Country
{ {
CountryId = reader.GetGuid(reader.GetOrdinal("CountryId")), CountryId = reader.GetGuid(reader.GetOrdinal("CountryId")),
CountryName = reader.GetString(reader.GetOrdinal("CountryName")), CountryName =
ISO3166_1 = reader.GetString(reader.GetOrdinal("ISO3166_1")), reader.GetString(reader.GetOrdinal("CountryName")),
ISO3166_1 =
reader.GetString(reader.GetOrdinal("ISO3166_1")),
}, },
}, },
}; };
} }
private static void AddParameter(DbCommand command, string name, object? value) private static void AddParameter(DbCommand command, string name,
object? value)
{ {
DbParameter p = command.CreateParameter(); DbParameter p = command.CreateParameter();
p.ParameterName = name; p.ParameterName = name;