Clear        


                
                    using BLL.DAL;
using BLL.Models;
using BLL.Services.Bases;
using Microsoft.EntityFrameworkCore;

namespace BLL.Services
{
    // Abstract Classes or Interfaces Way 1: Creating an interface for every concrete service class
    ///// <summary>
    ///// Performs role CRUD operations.
    ///// </summary>
    //public interface IRoleService // summary can be used for displaying the explanation when mouse is hovered over this type (IRoleService) anywhere in the project,
    //                              // summaries can be written above interfaces, classes, methods and properties by hitting "/" character on the keyboard 3 times
    //{
    //    // method definitions: method definitions must be created here for what the class implements this interface can do and to be used in the controllers

    //    /// <summary>
    //    /// Queries and returns role data query from the Roles table.
    //    /// </summary>
    //    /// <returns>IQueryable</returns>
    //    public IQueryable<RoleModel> Query();

    //    /// <summary>
    //    /// Inserts the role data to the Roles table. IsSuccessful and Message properties can be used for the operation result after method invocation.
    //    /// </summary>
    //    /// <param name="role"></param>
    //    /// <returns>Service</returns>
    //    public Service Create(Role role);

    //    /// <summary>
    //    /// Updates the role data in the Roles table by role parameter's Id value. IsSuccessful and Message properties can be used for the operation result after method invocation.
    //    /// </summary>
    //    /// <param name="role"></param>
    //    /// <returns>Service</returns>
    //    public Service Update(Role role);

    //    /// <summary>
    //    /// Deletes the role data in the Roles table by role id parameter. IsSuccessful and Message properties can be used for the operation result after method invocation.
    //    /// </summary>
    //    /// <param name="id"></param>
    //    /// <returns>Service</returns>
    //    public Service Delete(int id);
    //}

    // Abstract Classes or Interfaces Way 1:
    //public class RoleService : Service, IRoleService // RoleService is a Service and implements IRoleService
    // Abstract Classes or Interfaces Way 2: Instead of creating an interface for every concrete service class,
    // we can implement the IService generic interface within the Bases folder
    public class RoleService : Service, IService<Role, RoleModel> // RoleService is a Service and implements IService
    {
        public RoleService(Db db) : base(db) // DbContext object base service class Constructor Injection
        {
        }



        // Method implementations of the method definitions in the interface:
        public IQueryable<RoleModel> Query() // Query method will be used for generating SQL queries without executing them.
        {
            // LINQ (Language Integrated Query) Select method maps the properties of RoleModel from the properties of r (Role) delegate
            // which is called Func in C# that returns a result of the specified type of the method.
            // The r delegates used in the return statement methods below are all Func delegates.
            // The other delegate type in C# is Action which doesn't return anything (void) and is generally used for configuration operations.
            // Mapping entity properties to model properties is called projection.
            // Way 1:
            //return _db.Roles.Select(r => new RoleModel() { Record = r }); // "r =>" is called Lambda Expression
            // Way 2: LINQ OrderBy method orders the records by the r (Role) delegate property (r.Name: Role Name),
            // OrderBy orders records in ascending, OrderByDescending orders records in descending order.
            // From the Roles DbSet collection, first order Role entities by Name in ascending order,
            // then for each element of Role entity collection map Role entity to the Record property (projection) of the RoleModel,
            // finally return the query.
            // Retrieving the query containing only the ordered role data:
            //return _db.Roles.OrderBy(r => r.Name).Select(r => new RoleModel() { Record = r });
            // Way 3: Retrieving the query containing the role data joined with the user data (Include method must be used):
            // An entity's Navigational Properties can be used as Lambda Expressions in the Include methods for retrieving their data with the entity data.
            // Including the relational data on demand is called Eager Loading in Entity Framework and it is used as default.
            // On the contrary, Lazy Loading in Entity Framework always includes the relational data (not recommended) and must be configured before for the usage.
            return _db.Roles.Include(r => r.Users).OrderBy(r => r.Name).Select(r => new RoleModel() { Record = r });

            // Generally used LINQ methods:
            // OrderBy, OrderByDescending, ThenBy, ThenByDescending, Where, Any, SingleOrDefault and Select.
            // LINQ methods can be used for any collection type such as DbSet, IQueryable, IEnumerable, List and arrays, even for strings.
        }

        public Service Create(Role role) // role parameter is the model that the user enters data in the view
        {
            // Role names must be unique in the Roles table, therefore we should check if the role with the same name exists:
            // LINQ SingleOrDefault method returns the single record (object) that matches the condition (r => r.Name == role.Name.Trim()),
            // if no record matching the condition is found, returns null, if more than one records matching the condition are found, throws exception.
            // Instead of SingleOrDefault, LINQ FirstOrDefault method can be used for retrieving the first record and
            // LINQ LastOrDefault method can be used for retrieving the last record,
            // FirstOrDefault and LastOrDefault don't throw an exception if more than one records matching the condition are found and
            // if no record matching the condition is found, they return null.
            // Alternatively LINQ Single, LINQ First and LINQ Last methods can be used which throw exception if no record matching the condition is found.
            // Generally, methods ending with OrDefault that return a null result when no elements are found
            // are used when dealing with a situation where no match is expected.
            // Trim string method trims the white space characters from the beginning and end of a string value.
            // LINQ Any method returns true if at least one record matches the condition (r => r.Name == role.Name.Trim()), otherwise returns false.
            // Way 1:
            //Role entity = _db.Roles.SingleOrDefault(r => r.Name == role.Name.Trim());
            //if (entity is not null)
            //    return Error("Role with the same name exists!");
            // Way 2:
            if (_db.Roles.Any(r => r.Name == role.Name.Trim()))
                return Error("Role with the same name exists!"); // will return a Service instance with the Message as the parameter and IsSuccessful false

            role.Name = role.Name.Trim(); // trimming of role name data that the user enters to be saved in the table without white space characters

            // Role entity insert operation:
            _db.Roles.Add(role); // we add the Role entity to the Roles DbSet saying Entity Framework that this Role entity will be inserted to the Roles table,
                                 // no changes to the Roles table are committed with this operation

            _db.SaveChanges(); // we invoke this method for committing all DbSet changes to the database with one operation, this is called Unit of Work

            return Success("Role created successfully."); // will return a Service instance with the Message as the parameter and IsSuccessful true
        }

        public Service Update(Role role)
        {
            // Checking if a record with the name other than the role paramater's name exists in the Roles table: we perform this check by adding the Id condition
            if (_db.Roles.Any(r => r.Id != role.Id && r.Name == role.Name.Trim()))
                return Error("Role with the same name exists!");

            // retrieving the Role entity from the Roles table:
            // Way 1:
            //Role entity = _db.Roles.Find(role.Id); // DbSet collections' Find method can also be used for retrieving a record by primary key values (here Id),
                                                     // if no record is found, returns null
            // Way 2: we will prefer using SingleOrDefault
            Role entity = _db.Roles.SingleOrDefault(r => r.Id == role.Id);

            // updating the Role entity property values with the role parameter property values that the user enters:
            entity.Name = role.Name.Trim();

            // Role entity update operation:
            _db.Roles.Update(entity); // we say Entity Framework that this Role entity will be updated (modified) in the Roles table,
                                      // no changes to the Roles table are committed with this operation

            _db.SaveChanges(); // we invoke this method for committing all DbSet changes to the database with one operation, this is called Unit of Work

            return Success("Role updated successfully."); // will return a Service instance with the Message as the parameter and IsSuccessful true
        }

        public Service Delete(int id)
        {
            // Checking if the role record to be deleted has relational users. If any, we shouldn't delete the role record:
            // Include method retrieves the relational data for entity's Navigation Property (r.Users) with the query.
            // In other words, we get the role records joined with user records.
            Role entity = _db.Roles.Include(r => r.Users).SingleOrDefault(r => r.Id == id);

            // Way 1:
            //if (entity.Users.Count > 0) // a List object's Count property returns the element count
            //    return Error("Role has relational users!");
            // Way 2:
            if (entity.Users.Any()) // if role has any relational users
                return Error("Role has relational users!");

            // Role entity delete operation:
            _db.Roles.Remove(entity); // we say Entity Framework that this Role entity will be deleted in the Roles table,
                                      // no changes to the Roles table are committed with this operation

            _db.SaveChanges(); // we invoke this method for committing all DbSet changes to the database with one operation, this is called Unit of Work

            return Success("Role deleted successfully."); // will return a Service instance with the Message as the parameter and IsSuccessful true
        }
    }
}