Refactor UserAccount repository methods and add stored procedures for user account management

This commit is contained in:
Aaron Po
2025-12-06 23:06:13 -05:00
parent 00a0f6c4ef
commit 8d6b903aa7
5 changed files with 146 additions and 221 deletions

View File

@@ -18,239 +18,34 @@ namespace DataAccessLayer
?? throw new InvalidOperationException(
"The connection string is not set in the environment variables."
);
}
public void Add(UserAccount userAccount)
{
const string query =
@"INSERT INTO UserAccount (UserAccountID, Username, FirstName, LastName, Email, CreatedAt, UpdatedAt, DateOfBirth, Timer)
VALUES (@UserAccountID, @Username, @FirstName, @LastName, @Email, @CreatedAt, @UpdatedAt, @DateOfBirth, @Timer);";
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(query, connection))
{
_ = command.Parameters.AddWithValue(
"@UserAccountID",
userAccount.UserAccountID
);
_ = command.Parameters.AddWithValue(
"@Username",
userAccount.Username
);
_ = command.Parameters.AddWithValue(
"@FirstName",
userAccount.FirstName
);
_ = command.Parameters.AddWithValue(
"@LastName",
userAccount.LastName
);
_ = command.Parameters.AddWithValue(
"@Email",
userAccount.Email
);
_ = command.Parameters.AddWithValue(
"@CreatedAt",
userAccount.CreatedAt
);
_ = command.Parameters.AddWithValue(
"@UpdatedAt",
userAccount.UpdatedAt ?? (object)DBNull.Value
);
_ = command.Parameters.AddWithValue(
"@DateOfBirth",
userAccount.DateOfBirth
);
_ = command.Parameters.AddWithValue(
"@Timer",
userAccount.Timer ?? (object)DBNull.Value
);
connection.Open();
_ = command.ExecuteNonQuery();
}
}
public UserAccount? GetById(Guid id)
{
const string query =
"SELECT * FROM UserAccount WHERE UserAccountID = @UserAccountID;";
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(query, connection))
{
_ = command.Parameters.AddWithValue("@UserAccountID", id);
connection.Open();
using (var reader = command.ExecuteReader())
{
if (reader.Read())
{
return new UserAccount
{
UserAccountID = reader.GetGuid(
reader.GetOrdinal("UserAccountID")
),
Username = reader.GetString(
reader.GetOrdinal("Username")
),
FirstName = reader.GetString(
reader.GetOrdinal("FirstName")
),
LastName = reader.GetString(
reader.GetOrdinal("LastName")
),
Email = reader.GetString(
reader.GetOrdinal("Email")
),
CreatedAt = reader.GetDateTime(
reader.GetOrdinal("CreatedAt")
),
UpdatedAt = reader.IsDBNull(
reader.GetOrdinal("UpdatedAt")
)
? null
: reader.GetDateTime(
reader.GetOrdinal("UpdatedAt")
),
DateOfBirth = reader.GetDateTime(
reader.GetOrdinal("DateOfBirth")
),
Timer = reader.IsDBNull(reader.GetOrdinal("Timer"))
? null
: (byte[])reader["Timer"],
};
}
}
}
return null;
}
public void Update(UserAccount userAccount)
{
const string query =
@"UPDATE UserAccount
SET Username = @Username, FirstName = @FirstName, LastName = @LastName, Email = @Email, CreatedAt = @CreatedAt, UpdatedAt = @UpdatedAt, DateOfBirth = @DateOfBirth, Timer = @Timer
WHERE UserAccountID = @UserAccountID;";
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(query, connection))
{
_ = command.Parameters.AddWithValue(
"@UserAccountID",
userAccount.UserAccountID
);
_ = command.Parameters.AddWithValue(
"@Username",
userAccount.Username
);
_ = command.Parameters.AddWithValue(
"@FirstName",
userAccount.FirstName
);
_ = command.Parameters.AddWithValue(
"@LastName",
userAccount.LastName
);
_ = command.Parameters.AddWithValue(
"@Email",
userAccount.Email
);
_ = command.Parameters.AddWithValue(
"@CreatedAt",
userAccount.CreatedAt
);
_ = command.Parameters.AddWithValue(
"@UpdatedAt",
userAccount.UpdatedAt ?? (object)DBNull.Value
);
_ = command.Parameters.AddWithValue(
"@DateOfBirth",
userAccount.DateOfBirth
);
_ = command.Parameters.AddWithValue(
"@Timer",
userAccount.Timer ?? (object)DBNull.Value
);
connection.Open();
_ = command.ExecuteNonQuery();
}
}
public void Delete(Guid id)
{
const string query =
"DELETE FROM UserAccount WHERE UserAccountID = @UserAccountID;";
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(query, connection))
{
_ = command.Parameters.AddWithValue("@UserAccountID", id);
connection.Open();
_ = command.ExecuteNonQuery();
}
}
public IEnumerable<UserAccount> GetAll()
{
const string query = "SELECT * FROM UserAccount;";
var userAccounts = new List<UserAccount>();
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(query, connection))
return new List<UserAccount>
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var userAccount = new UserAccount
{
UserAccountID = reader.GetGuid(
reader.GetOrdinal("UserAccountID")
),
Username = reader.GetString(
reader.GetOrdinal("Username")
),
FirstName = reader.GetString(
reader.GetOrdinal("FirstName")
),
LastName = reader.GetString(
reader.GetOrdinal("LastName")
),
Email = reader.GetString(
reader.GetOrdinal("Email")
),
CreatedAt = reader.GetDateTime(
reader.GetOrdinal("CreatedAt")
),
UpdatedAt = reader.IsDBNull(
reader.GetOrdinal("UpdatedAt")
)
? null
: reader.GetDateTime(
reader.GetOrdinal("UpdatedAt")
),
DateOfBirth = reader.GetDateTime(
reader.GetOrdinal("DateOfBirth")
),
Timer = reader.IsDBNull(reader.GetOrdinal("Timer"))
? null
: (byte[])reader["Timer"],
};
userAccounts.Add(userAccount);
}
}
}
return userAccounts;
}
}
}

View File

@@ -0,0 +1,100 @@
USE Biergarten;
GO
CREATE OR ALTER PROCEDURE usp_CreateUserAccount
(
@Username VARCHAR(64),
@FirstName NVARCHAR(128),
@LastName NVARCHAR(128),
@DateOfBirth DATETIME,
@Email VARCHAR(128)
)
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
INSERT INTO UserAccount
(
Username,
FirstName,
LastName,
DateOfBirth,
Email
)
VALUES
(
@Username,
@FirstName,
@LastName,
@DateOfBirth,
@Email
);
COMMIT TRANSACTION
END;
GO
CREATE OR ALTER PROCEDURE usp_DeleteUserAccount
(
@UserAccountId INT
)
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
IF NOT EXISTS (SELECT 1 FROM UserAccount WHERE UserAccountId = @UserAccountId)
BEGIN
RAISERROR('UserAccount with the specified ID does not exist.', 16,
1);
ROLLBACK TRANSACTION
RETURN
END
DELETE FROM UserAccount
WHERE UserAccountId = @UserAccountId;
COMMIT TRANSACTION
END;
GO
CREATE OR ALTER PROCEDURE usp_UpdateUserAccount
(
@Username VARCHAR(64),
@FirstName NVARCHAR(128),
@LastName NVARCHAR(128),
@DateOfBirth DATETIME,
@Email VARCHAR(128),
@UserAccountId GUID
)
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
IF NOT EXISTS (SELECT 1 FROM UserAccount WHERE UserAccountId = @UserAccountId)
BEGIN
RAISERROR('UserAccount with the specified ID does not exist.', 16,
1);
ROLLBACK TRANSACTION
RETURN
END
UPDATE UserAccount
SET
Username = @Username,
FirstName = @FirstName,
LastName = @LastName,
DateOfBirth = @DateOfBirth,
Email = @Email
WHERE UserAccountId = @UserAccountId;
COMMIT TRANSACTION
END;
GO

View File

@@ -22,7 +22,7 @@ USE Biergarten;
----------------------------------------------------------------------------
----------------------------------------------------------------------------
CREATE TABLE UserAccount
CREATE TABLE dbo.UserAccount
(
UserAccountID UNIQUEIDENTIFIER
CONSTRAINT DF_UserAccountID DEFAULT NEWID(),

View File

@@ -1,3 +1,33 @@
// Load a local .env file into environment variables when present (useful for local development)
try
{
var envPath = Path.Combine(Directory.GetCurrentDirectory(), ".env");
if (File.Exists(envPath))
{
foreach (var line in File.ReadAllLines(envPath))
{
var trimmed = line.Trim();
if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith("#"))
continue;
var idx = trimmed.IndexOf('=');
if (idx <= 0)
continue;
var key = trimmed.Substring(0, idx).Trim();
var val = trimmed.Substring(idx + 1).Trim();
if (val.Length >= 2 && ((val.StartsWith("\"") && val.EndsWith("\"")) || (val.StartsWith("'") && val.EndsWith("'"))))
{
val = val.Substring(1, val.Length - 2);
}
if (Environment.GetEnvironmentVariable(key) == null)
Environment.SetEnvironmentVariable(key, val);
}
}
}
catch
{
// If dotenv loading fails, continue without blocking startup.
}
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.

View File

@@ -3,15 +3,15 @@ services:
image: mcr.microsoft.com/mssql/server:2022-latest
platform: linux/amd64
container_name: sqlserver
env_file:
- .env
environment:
ACCEPT_EULA: "Y"
SA_PASSWORD: "YourStrong!Passw0rd"
ports:
- "1433:1433"
SA_PASSWORD: "${SA_PASSWORD}"
volumes:
- sqlserverdata:/var/opt/mssql
healthcheck:
test: [ "CMD", "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "YourStrong!Passw0rd", "-Q", "SELECT 1" ]
test: [ "CMD", "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "${SA_PASSWORD}", "-Q", "SELECT 1" ]
interval: 10s
timeout: 5s
retries: 12
@@ -21,8 +21,8 @@ services:
redis:
image: redis:7
container_name: redis
ports:
- "6379:6379"
env_file:
- .env
networks:
- devnet
healthcheck:
@@ -32,7 +32,7 @@ services:
retries: 5
dotnet:
image: mcr.microsoft.com/dotnet/sdk:9.0
image: mcr.microsoft.com/dotnet/sdk:10.0
container_name: dotnet-sdk
tty: true
stdin_open: true
@@ -45,8 +45,8 @@ services:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
HOME: /home/dev
USER: dev
DB_CONNECTION_STRING: "Server=sqlserver,1433;User Id=sa;Password=YourStrong!Passw0rd;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;Database=Biergarten;"
REDIS_URL: "redis:6379"
DB_CONNECTION_STRING: "Server=sqlserver,1433;User Id=sa;Password=${SA_PASSWORD};Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;Database=${DB_NAME};"
REDIS_URL: "${REDIS_URL}"
user: root
networks:
- devnet