065a83442c
Enhanced the application's service layer by adding FormValidationService and EventDefinitionService to the dependency injection container in Program.cs. Updated Create, Edit, and other relevant components to utilize these services for improved form validation and event processing functionality.
176 lines
6.7 KiB
C#
176 lines
6.7 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
using Microsoft.AspNetCore.Components.Forms;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using MudBlazor;
|
|
|
|
namespace WebApp.Services;
|
|
|
|
/// <summary>
|
|
/// Service for handling form validation errors and displaying them to users
|
|
/// </summary>
|
|
public class FormValidationService
|
|
{
|
|
private readonly ISnackbar _snackbar;
|
|
|
|
public FormValidationService(ISnackbar snackbar)
|
|
{
|
|
_snackbar = snackbar;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collects all validation errors from an EditContext and model validation
|
|
/// </summary>
|
|
/// <param name="editContext">The EditContext from the form</param>
|
|
/// <param name="model">The model being validated</param>
|
|
/// <returns>List of validation error messages</returns>
|
|
public List<string> CollectValidationErrors(EditContext editContext, object model)
|
|
{
|
|
var errors = new List<string>();
|
|
|
|
// Validate the entire model using data annotations
|
|
var validationResults = new List<ValidationResult>();
|
|
var validationContext = new ValidationContext(model);
|
|
bool isValid = Validator.TryValidateObject(model, validationContext, validationResults, true);
|
|
|
|
if (!isValid)
|
|
{
|
|
foreach (var result in validationResults)
|
|
{
|
|
foreach (var memberName in result.MemberNames)
|
|
{
|
|
var errorMessage = $"{memberName}: {result.ErrorMessage}";
|
|
if (!errors.Contains(errorMessage))
|
|
{
|
|
errors.Add(errorMessage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also get validation messages from EditContext (this gets all field-level messages)
|
|
var validationMessages = editContext.GetValidationMessages();
|
|
foreach (var message in validationMessages)
|
|
{
|
|
if (!errors.Contains(message))
|
|
{
|
|
errors.Add(message);
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles invalid form submission by collecting errors and displaying them
|
|
/// </summary>
|
|
/// <param name="editContext">The EditContext from the form</param>
|
|
/// <param name="model">The model being validated</param>
|
|
/// <param name="onErrorsCollected">Optional callback to handle the collected errors (e.g., store in component state)</param>
|
|
/// <returns>List of validation error messages</returns>
|
|
public List<string> HandleInvalidSubmit(EditContext editContext, object model, Action<List<string>>? onErrorsCollected = null)
|
|
{
|
|
var errors = CollectValidationErrors(editContext, model);
|
|
|
|
if (onErrorsCollected != null)
|
|
{
|
|
onErrorsCollected(errors);
|
|
}
|
|
|
|
if (errors.Any())
|
|
{
|
|
var errorText = string.Join("; ", errors);
|
|
_snackbar.Add($"Validation failed: {errorText}", Severity.Error);
|
|
}
|
|
else
|
|
{
|
|
_snackbar.Add("Please correct the validation errors and try again.", Severity.Warning);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles DbUpdateException and displays user-friendly error messages
|
|
/// </summary>
|
|
/// <param name="ex">The DbUpdateException to handle</param>
|
|
/// <param name="defaultErrorMessage">Default error message if specific handling isn't available</param>
|
|
/// <param name="entityName">Name of the entity being saved (e.g., "event", "student", "team")</param>
|
|
/// <param name="operation">Operation being performed (e.g., "saving", "creating")</param>
|
|
/// <param name="uniqueConstraintField">Field name for UNIQUE constraint errors (e.g., "name", "email")</param>
|
|
public void HandleDbUpdateException(
|
|
DbUpdateException ex,
|
|
string defaultErrorMessage,
|
|
string entityName = "record",
|
|
string operation = "saving",
|
|
string? uniqueConstraintField = null)
|
|
{
|
|
var errorMessage = defaultErrorMessage;
|
|
|
|
if (ex.InnerException != null)
|
|
{
|
|
var innerMessage = ex.InnerException.Message;
|
|
|
|
if (innerMessage.Contains("UNIQUE constraint", StringComparison.OrdinalIgnoreCase) ||
|
|
innerMessage.Contains("duplicate key", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (!string.IsNullOrEmpty(uniqueConstraintField))
|
|
{
|
|
errorMessage = $"A {entityName} with this {uniqueConstraintField} already exists. Please choose a different {uniqueConstraintField}.";
|
|
}
|
|
else
|
|
{
|
|
errorMessage = $"A {entityName} with this information already exists. Please check for duplicates.";
|
|
}
|
|
}
|
|
else if (innerMessage.Contains("FOREIGN KEY constraint", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
errorMessage = $"Cannot {operation}: This {entityName} is referenced by other records.";
|
|
}
|
|
else
|
|
{
|
|
errorMessage = $"Database error: {innerMessage}";
|
|
}
|
|
}
|
|
|
|
_snackbar.Add(errorMessage, Severity.Error);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles generic exceptions and displays user-friendly error messages
|
|
/// </summary>
|
|
/// <param name="ex">The Exception to handle</param>
|
|
public void HandleException(Exception ex)
|
|
{
|
|
var errorMessage = $"An unexpected error occurred: {ex.Message}";
|
|
_snackbar.Add(errorMessage, Severity.Error);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles DbUpdateConcurrencyException and displays user-friendly error messages
|
|
/// </summary>
|
|
/// <param name="ex">The DbUpdateConcurrencyException to handle</param>
|
|
/// <param name="entityExists">Function to check if the entity still exists</param>
|
|
/// <param name="entityName">Name of the entity (e.g., "event", "student", "team")</param>
|
|
/// <param name="onNotFound">Optional action to perform when entity is not found (e.g., navigate to notfound page)</param>
|
|
/// <returns>True if the exception was handled (entity not found), false if it should be rethrown</returns>
|
|
public bool HandleDbUpdateConcurrencyException(
|
|
DbUpdateConcurrencyException ex,
|
|
Func<bool> entityExists,
|
|
string entityName = "record",
|
|
Action? onNotFound = null)
|
|
{
|
|
if (!entityExists())
|
|
{
|
|
_snackbar.Add($"{entityName.Substring(0, 1).ToUpper()}{entityName.Substring(1)} not found. It may have been deleted.", Severity.Error);
|
|
onNotFound?.Invoke();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
_snackbar.Add($"The {entityName} was modified by another user. Please refresh and try again.", Severity.Warning);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|