Clear        


                
                    using CORE.Domain;
using CORE.Services;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Users.APP.Domain;
using Users.APP.Features.Roles;
using Users.APP.Features.Statuses;

namespace Users.APP.Features.Users
{
    // Request properties are created according to the data that
    // will be retrieved from APIs.
    public class UserQueryRequest : Record,
        IRequest<IQueryable<UserQueryResponse>>
    {
    }

    // Response properties are created according to the data to be
    // presented in API responses.
    public class UserQueryResponse : Record
    {
        // Copy all the non navigation properties from User entity.
        public string UserName { get; set; }

        public string Password { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public Genders Gender { get; set; }

        public DateTime? BirthDate { get; set; }

        public DateTime RegistrationDate { get; set; }

        public double Score { get; set; }

        public bool IsOnline { get; set; }

        public int StatusId { get; set; }



        // Add the new properties ending with R declaring Response
        // for custom properties or formatted string value properties.
        public string IsOnlineR { get; set; }

        public string ScoreR { get; set; }

        public string RegistrationDateR { get; set; }

        public string BirthDateR { get; set; }

        public string GenderR { get; set; }

        // FirstName + " " + LastName concatenated value.
        public string FullNameR { get; set; }

        // Password value hidden with * characters.
        public string PasswordR { get; set; }




        // Way 1:
        // Property for the Title value of the
        // related Status entity.
        public string StatusTitleR { get; set; }

        // Way 2:
        // Property for the mapped StatusQueryResponse object
        // from the related Status entity.
        // Either Way 1, Way 2 or both can be used.
        public StatusQueryResponse StatusR { get; set; }



        // Way 1:
        // Property for the concatenated role names
        // of related Role entities.
        public string RoleNamesR { get; set; }

        // Way 2:
        // Property for the RoleQueryResponse objects projected
        // from the related Role entities.
        // Either Way 1, Way 2 or both can be used.
        public List<RoleQueryResponse> RolesR { get; set; }
    }

    // UserQueryHandler inherits the DbService class for User entity type,
    // therefore User entity database operations of the DbService
    // class can be used in this class.
    // UserQueryHandler also implements the Mediator IRequestHandler
    // interface for UserQueryRequest and IQueryable of UserQueryResponse
    // types, therefore IRequestHandler interface's Handle method
    // definition can be implemented for types UserQueryRequest and
    // IQueryable of UserQueryResponse in this class, which will be used by
    // the related controller's action.
    public class UserQueryHandler : DbService<User>,
        IRequestHandler<UserQueryRequest,
            IQueryable<UserQueryResponse>>
    {
        // Constructor that passes the injected DbContext instance
        // to the DbService base class constructor.
        public UserQueryHandler(DbContext db) : base(db)
        {
        }

        // Overrides the base virtual DbQuery method to include
        // Status, UserRole and Role entities data to the query
        // to be used in the Handle method and orders the query
        // by User entity Score property descending first, then
        // the ordered records are ordered descending by User
        // entity RegistrationDate property. Finally the ordered
        // records are ordered ascending by User entity UserName
        // property.
        // The overridden DbQuery method can be used in any method
        // below by invoking the DbQuery method.
        protected override IQueryable<User> DbQuery()
        {
            // Relationships:
            // User <-> Status.
            // User <-> UserRoles <-> Roles.
            return base.DbQuery()
                .Include(u => u.Status) // Include Status entities
                                        // from User entities.
                .Include(u => u.UserRoles) // Include UserRole entities
                                           // from User entities.
                .ThenInclude(ur => ur.Role) // Then include Role entities
                                            // from UserRole entities.
                .OrderByDescending(u => u.Score)
                .ThenByDescending(u => u.RegistrationDate)
                .ThenBy(u => u.UserName);
            // Only one OrderBy method must be called.
            // The other called ordering methods must be ThenBy.
            // u: User entity delegate, ur: UserRole entity delegate.
        }

        // Gets the updated query from the DbQuery method then projects
        // the User entities to user query responses and returns the
        // UserQueryResponse query.
        // The returned query can be executed by invoking ToListAsync,
        // SingleOrDefaultAsync, FirstOrDefaultAsync, AnyAsync, etc.
        // LINQ (Language Integrated Query) methods and then the
        // returned result can be used.
        public Task<IQueryable<UserQueryResponse>> Handle(
            UserQueryRequest request, CancellationToken cancellationToken)
        {
            return Task.FromResult(
                DbQuery().Select(u => new UserQueryResponse
            {
                // Map each User entity property to UserQueryResponse property.
                Id = u.Id,
                UserName = u.UserName,
                Password = u.Password,
                FirstName = u.FirstName,
                LastName = u.LastName,
                RegistrationDate = u.RegistrationDate,
                BirthDate = u.BirthDate,
                IsOnline = u.IsOnline,
                Gender = u.Gender,
                Score = u.Score,
                StatusId = u.StatusId,
                // Way 1: Map primitive type data for basic information.
                StatusTitleR = u.Status.Title,
                // Way 2: Map complex type object for more information.
                // Either Way 1, Way 2 or both can be used.
                StatusR = new StatusQueryResponse
                {
                    // Map each Status entity property to
                    // UserQueryResponse's StatusQueryResponse property.
                    Id = u.Status.Id,
                    Title = u.Status.Title
                },
                // Way 1: Map primitive type data for basic information.
                RoleNamesR = string.Join(", ", u.UserRoles
                     .OrderBy(ur => ur.Role.Name)
                     .Select(ur => ur.Role.Name)),
                // Way 2: Map complex type object for more information.
                // Either Way 1, Way 2 or both can be used.
                RolesR = u.UserRoles.OrderBy(ur => ur.Role.Name)
                     .Select(ur => new RoleQueryResponse
                     {
                         // Map each Role entity property to
                         // UserQueryResponse's RoleQueryResponse property
                         // through UserRoles.
                         Id = ur.Role.Id,
                         Name = ur.Role.Name
                     }).ToList(),
                // Hidden password with * character.
                PasswordR = new string('*', u.Password.Length),
                // Concatenated first name and list name with white space character.
                FullNameR = u.FirstName + " " + u.LastName,
                // Formatted date and time in month/day/year hour:minute:second
                // format. No need to use the CultureInfo instance for the second
                // parameter of the ToString method since CultureInfo has been
                // assigned in the base abstract Service class by the Culture
                // property assignment. If Culture property of the base abstract
                // Service class is changed within this class constructor,
                // the changed value will be used by the CultureInfo instance and
                // then ToString method.
                RegistrationDateR = u.RegistrationDate
                     .ToString("MM/dd/yyyy HH:mm.ss"),
                // Ternary operator:
                BirthDateR = u.BirthDate.HasValue ? // if (u.BirthDate != null).
                     u.BirthDate.Value.ToShortDateString() : // Assign u.BirthDate's
                                                             // value since u.BirthDate
                                                             // is nullable.
                     string.Empty, // "" string.
                // ToShortDateString returns the date in month/day/year format.
                IsOnlineR = u.IsOnline ? "Yes" : "No",
                GenderR = u.Gender.ToString(), // Assigns "Man" or "Woman".
                ScoreR = u.Score.ToString("N1") // N: Number format.
                                                // 1: 1 decimal.
                                                // C can also be used for currency.
                                                // No need to define the second
                                                // CultureInfo parameter since Culture
                                                // property has been assigned in the
                                                // base abstract Service class.
            })); // u: User entity delegate, ur: UserRole entity delegate.
        }
    }
}