using CORE.APP.Models;
using CORE.APP.Services;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Users.APP.Domain;
using Users.APP.Features.Users;
namespace Users.APP.Features.Roles
{
public class RoleQueryRequest : Request, IRequest<IQueryable<RoleQueryResponse>>
{
}
// response properties are created according to the data to be presented in API responses or UIs
public class RoleQueryResponse : Response
{
// copy all the non navigation properties from Role entity
public string Name { get; set; }
// add the new properties, some ending with F for the properties with the same name, for custom or formatted string values
// Way 1: Use user related properties for partial data
public int UserCount { get; set; }
public string UsersF { get; set; }
// Way 2: Use user related UserQueryResponse collection property for full data
// Either way 1, way 2 or both of them can be used
public List<UserQueryResponse> Users { get; set; }
}
// Inherit from the generic entity service class therefore DbContext injected constructor can be automatically created
// and entity CRUD (create, read, update, delete) methods in the base class can be invoked.
public class RoleQueryHandler : Service<Role>, IRequestHandler<RoleQueryRequest, IQueryable<RoleQueryResponse>>
{
public RoleQueryHandler(DbContext db) : base(db)
{
}
// base virtual DbSet method is overriden therefore the overriden entity query can be used in all other methods
protected override IQueryable<Role> DbSet()
{
// r: Role entity delegate, ur: UserRole entity delegate
return base.DbSet() // will return Roles DbSet
.Include(r => r.UserRoles).ThenInclude(ur => ur.User) // will first include the relational UserRoles then User data
.OrderBy(r => r.Name); // query will be ordered ascending by Name values
// Include, ThenInclude, OrderBy, OrderByDescending, ThenBy and ThenByDescending methods can also be used with DbSets.
}
public Task<IQueryable<RoleQueryResponse>> Handle(RoleQueryRequest request, CancellationToken cancellationToken)
{
var query = DbSet().Select(r => new RoleQueryResponse()
{
// assigning entity properties to the response
Id = r.Id,
Name = r.Name,
// assigning custom or formatted properties to the response
// Way 1: Assign user related properties for partial data
UserCount = r.UserRoles.Count, // returns the users count of each role
UsersF = string.Join(", ", r.UserRoles.Select(ur => ur.User.UserName)), // returns a comma seperated user names string for each role
// Way 2: Assign user related UserQueryResponse collection property for full data
// Either way 1, way 2 or both of them can be applied
Users = r.UserRoles.Select(ur => new UserQueryResponse
{
// assign User entity properties to the response through each UserRole entity's User entity property
Id = ur.User.Id,
UserName = ur.User.UserName,
Password = ur.User.Password,
IsActive = ur.User.IsActive,
RegistrationDate = ur.User.RegistrationDate,
BirthDate = ur.User.BirthDate,
Score = ur.User.Score,
FirstName = ur.User.FirstName,
LastName = ur.User.LastName,
Gender = ur.User.Gender,
Address = ur.User.Address,
GroupId = ur.User.GroupId,
RoleIds = ur.User.RoleIds,
CountryId = ur.User.CountryId,
CityId = ur.User.CityId,
// assigning custom or formatted User entity properties through each UserRole entity's
// User entity property to the response
// concatenated first name and last name with a white space character
FullName = ur.User.FirstName + " " + ur.User.LastName,
// assign "Active" or "Inactive" for IsActive boolean property using ternary operator
IsActiveF = ur.User.IsActive ? "Active" : "Inactive",
// If User entity's BirthDate value is not null, convert and assign the value with month/day/year format, otherwise assign "".
// No need to give the second CultureInfo parameter (e.g. new CultureInfo("tr-TR")) to the ToString method since
// CultureInfo property was assigned in the constructor of the base or this class.
// Instead of ToString method, ToShortDateString (e.g. 08/18/2025) or ToLongDateString (e.g. Monday, August 18, 2025) methods can be used.
// For time ToShortTimeString (17:26) or ToLongTimeString (17:26:52) can be used.
// Again CultureInfo parameter is not needed for these methods.
BirthDateF = ur.User.BirthDate.HasValue ? ur.User.BirthDate.Value.ToString("MM/dd/yyyy") : string.Empty,
RegistrationDateF = ur.User.RegistrationDate.ToShortDateString(),
ScoreF = ur.User.Score.ToString("N1"), // N: number format, C: currency format, 1: one decimal
GenderF = ur.User.Gender.ToString() // will assign Woman or Man
}).ToList()
});
return Task.FromResult(query);
}
}
}