using CORE.Domain;
using CORE.Models;
using CORE.Services;
using MediatR;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using Users.APP.Domain;
namespace Users.APP.Features.Users
{
// Request properties are created according to the data that
// will be retrieved from APIs.
public class UserUpdateRequest : Record, IRequest<CommandResponse>
{
// Copy all the non navigation properties from User entity.
// UserName is required and can be minimum 3 maximum 30 characters.
[Required, StringLength(30, MinimumLength = 3)]
public string UserName { get; set; }
[Required, StringLength(15, MinimumLength = 3)]
public string Password { get; set; }
[StringLength(50)]
public string FirstName { get; set; }
[StringLength(50)]
public string LastName { get; set; }
public Genders Gender { get; set; }
public DateTime? BirthDate { get; set; }
// We don't need to get the RegistrationDate value from the client
// since it will be assigned automatically in UserAddHandler class.
[Required, Range(0, 5)]
public double? Score { get; set; }
// We don't need to get the IsOnline value from the client
// since it will be assigned automatically during token
// authentication.
// Modify (1 to many) or add (many to many) the relationship properties.
/*
The type changed from int to int? to be able to use [Required] attribute.
If the type is int, the default value will be 0 and the validation will
always be successful even if no value is assigned.
*/
// For users-status one to many relationship.
[Required]
public int? StatusId { get; set; }
// Required may not be defined if a user may not have a role.
// For users-roles many to many relationship.
[Required]
public List<int> RoleIds { get; set; }
}
// UserUpdateHandler inherits the DbService class for User entity type,
// therefore User entity database operations of the DbService
// class can be used in this class.
// UserUpdateHandler also implements the Mediator IRequestHandler
// interface for UserUpdateRequest and CommandResponse types,
// therefore IRequestHandler interface's Handle method
// definition can be implemented for types UserUpdateRequest and
// CommandResponse in this class, which will be used by
// the related controller's action.
public class UserUpdateHandler : DbService<User>,
IRequestHandler<UserUpdateRequest, CommandResponse>
{
// Constructor that passes the injected DbContext instance
// to the DbService base class constructor.
public UserUpdateHandler(DbContext db) : base(db)
{
}
// Override the DbQuery method of the base DbService class to
// include the UserRole entities to the query. Therefore
// during update operation, the related UserRole entities
// will be deleted first and then new related UserRole entities
// will be inserted through the RoleIds property of the
// UserUpdateRequest instance.
protected override IQueryable<User> DbQuery()
{
return base.DbQuery().Include(u => u.UserRoles);
// u: User entity delegate.
}
// Checks whether the user with the same trimmed and
// case sensitive user name other than the request record
// exists in the Users database table first. If it doesn't exist,
// gets the User entity by ID and if found updates the User
// entity mapped from the UserUpdateRequest instance in the Users
// database table.
public async Task<CommandResponse> Handle(UserUpdateRequest request,
CancellationToken cancellationToken)
{
if (await DbQuery().AnyAsync(u => u.Id != request.Id &&
u.UserName == request.UserName.Trim(), cancellationToken))
return Error("User with the same user name exists!");
// u: User entity delegate.
// Trim method removes white space characters in the
// beginning and at the end.
// Get the User entity by ID from the Users database table
// and check if it is null.
var userEntity = await DbSingleAsync(request.Id, cancellationToken);
if (userEntity is null)
return Error("User not found!");
// Delete the related UserRole entities first.
if (request.RoleIds is not null)
DbRemove(userEntity.UserRoles);
// Then update the User entity properties from the
// UserUpdateRequest instance then update the entity in the
// Users database table (third save default parameter
// of the DbUpdateAsync method is true therefore changes of the
// DbSets are commited to the related database tables).
userEntity.UserName = request.UserName.Trim();
userEntity.Password = request.Password.Trim();
userEntity.FirstName = request.FirstName?.Trim();
// Since request.FirstName may be null, ? is used after
// meaning that if request.FirstName is null, assign
// null to the User entity's FirstName property else
// assign the trimmed value of request.FirstName.
userEntity.LastName = request.LastName?.Trim();
// Since request.LastName may be null, ? is used after
// meaning that if request.LastName is null, assign
// null to the User entity's LastName property else
// assign the trimmed value of request.LastName.
// We don't need to update the RegistrationDate value
// since it will be assigned automatically in the
// UserAddHandler class.
userEntity.BirthDate = request.BirthDate;
userEntity.Gender = request.Gender;
userEntity.Score = request.Score ?? 0;
userEntity.StatusId = request.StatusId ?? 0;
// ??: Null coalescing operator: If request.StatusId is
// null assign 0 else assign request.StatusId value to the
// User entity StatusId property. request.StatusId.Value
// may also be used since request.StatusId is required.
// Same for request.Score.
userEntity.RoleIds = request.RoleIds;
await DbUpdateAsync(userEntity, cancellationToken);
return Success(userEntity.Id, "User updated successfully!");
}
}
}