Clear        


                
                    using CORE.Domain;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;

namespace CORE.Services
{
    // Abstract base class to perform CRUD (Create, Read, Update and Delete)
    // database operations for a specific entity type inherited from the
    // Record class and has a parameterless constructor.
    // Inherits from the Service class to manage culture and return
    // successful or failure operation result CommandResponse objects
    // after operations.
    // Implements the IDisposable interface to release unmanaged resources.
    public abstract class DbService<TEntity> : Service, IDisposable
        where TEntity : Record, new()
    {
        // Field for Dependency Injection for the DbContext instance to
        // perform database operations in the below methods.
        private readonly DbContext _db;

        // Constructor with an injected DbContext instance parameter 
        // for performing database operations in the below methods by
        // using the injected instance assigned _db field.
        protected DbService(DbContext db)
        {
            _db = db;
        }

        // Method to return the entity query such as "select * from table"
        // where table is the related entity DbSet.
        // Defined as virtual to allow derived classes to override the
        // query if needed such as for ordering, filtering, including
        // the related entities to the query, etc.
        // AsNoTracking is used for not tracking the returned entities to
        // improve performance. If not written, the default
        // behavior is tracking.
        protected virtual IQueryable<TEntity> DbQuery()
            => _db.Set<TEntity>().AsNoTracking();

        // Method to return a single tracked (AsTracking) entity for update
        // or delete operations by its ID using the base or derived overridden
        // DbQuery method. If the entity is not found, returns null since
        // OrDefault version of the Single method is used.
        protected TEntity DbSingle(int id)
        {
            return DbQuery().AsTracking()
                .SingleOrDefault(entity => entity.Id == id);
        }

        // Method to return a single tracked (AsTracking) entity
        // according to a lambda expression filter parameter (predicate)
        // which may contain one or more conditions using the base or
        // derived overridden DbQuery method. If the entity is not found,
        // returns null since OrDefault version of the Single method is used.
        protected TEntity DbSingle(Expression<Func<TEntity, bool>> predicate)
            => DbQuery().AsTracking().SingleOrDefault(predicate);

        // Method to persist changes to the database and return the number
        // of effected rows of the table.
        // Defined as virtual to allow derived classes to override the behavior
        // if needed such as for extra logging, error handling, etc.
        protected virtual int DbSave() => _db.SaveChanges();

        // Method to insert a new entity to the entity DbSet and persist changes
        // to the database if the save default parameter value is true.
        // If the save parameter value is false, the changes will not be
        // persisted to the database, which can be used for multiple insert
        // operations. Then DbSave method can be invoked to persist all changes
        // to the database once (Unit of Work) to increase performance.
        protected void DbAdd(TEntity entity, bool save = true)
        {
            _db.Set<TEntity>().Add(entity);
            if (save)
                DbSave();
        }

        // Method to update an existing entity retrieved by DbSingle method
        // and persist changes to the database if the save default parameter
        // value is true. If the save parameter value is false, the changes will
        // not be persisted to the database, which can be used for multiple
        // update operations. Then DbSave method can be invoked to persist all
        // changes to the database once (Unit of Work) to increase performance.
        protected void DbUpdate(TEntity entity, bool save = true)
        {
            _db.Set<TEntity>().Update(entity);
            if (save)
                DbSave();
        }

        // Method to delete an existing entity retrieved by DbSingle method
        // and persist changes to the database if the save default parameter
        // value is true. If the save parameter value is false, the changes will
        // not be persisted to the database, which can be used for multiple
        // delete operations. Then DbSave method can be invoked to persist all
        // changes to the database once (Unit of Work) to increase performance.
        protected void DbRemove(TEntity entity, bool save = true)
        {
            _db.Set<TEntity>().Remove(entity);
            if (save)
                DbSave();
        }

        // Method to delete the related navigation entities of an entity.
        // The changes will not be persisted to the database, therefore DbAdd,
        // DbUpdate, DbRemove or DbSave methods should be invoked after
        // to persist all changes to the database.
        protected void DbRemove<TNavigationEntity>(List<TNavigationEntity> 
            navigationEntities) where TNavigationEntity : Record, new()
        {
            _db.Set<TNavigationEntity>().RemoveRange(navigationEntities);
        }

        // Method to dispose the DbContext instance and suppress finalization
        // (don't call the finalizer) of the instance of this class to
        // improve performance and avoid memory leaks.
        public void Dispose()
        {
            _db.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}