MVC - 分層式架構(二)服務層

  • 1722
  • 0

此篇記錄MVC與自己實作的基本架構。

這裡的服務層基本上是針對資料處理層。


 

該Service的定義如同商業邏輯層 : 

  • 針對CRUD的商業邏輯應該布置在此
  • 若有特殊的處理應透過繼承服務層介面與類別的方式下去擴充
也就是說整體架構上CRUD透過了通用的Repository介面,Service層包著商業邏輯後去使用Repository類別
拿MVC的整體架構來說 : 
Controller 只要專注在流程控制,使用 Service 做想做的事情。
Service 只要專注在商業邏輯,使用 Repository 做想做的事情。
Repository 只要專注在各個DbContext的物件與各個Model的類別還有 CRUD 。

 

專案內容 : 

服務層-介面 : 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace MVC_Service.Interface
{
    /// <summary>
    /// Service服務層內容的Interface
    /// </summary>
    /// <typeparam name="T">主要要儲存的Entity Type</typeparam
    public interface IGenericService<T> : IDisposable where T : class
    {
        /// <summary>
        /// 依照某一個ViewModel的值,產生對應的Entity並且新增到資料庫
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="viewModel">ViewModel的Reference</param>
        /// <returns>是否儲存成功</returns>
        void CreateViewModelToDatabase<TViewModel>(TViewModel viewModel);

        /// <summary>
        /// 刪除某一筆Entity
        /// </summary>
        /// <param name="wherePredicate">過濾出要被刪除的Entity條件</param>
        /// <returns>是否刪除成功</returns>
        void Delete(Expression<Func<T, bool>> wherePredicate);

        /// <summary>
        /// 依照某一個ViewModel的值,覆寫對應的Entity
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="viewModel">ViewModel的值</param>
        /// <returns>是否更新成功</returns>
        void UpdateViewModelToDatabase<TViewModel>(TViewModel viewModel);

        /// <summary>
        /// 更新一筆資料的內容。只更新部分欄位的。
        /// Lambda 運算式 只需要傳遞欄位屬性 EX : x => x.ColumnName1, x => x.Column2....
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="updateProperties">需要更新的欄位。</param>
        void UpdateViewModelToDatabase<TViewModel>(TViewModel viewModel,
            Expression<Func<T, object>>[] updateProperties);

        /// <summary>
        /// 取得全部的資料 且將資料帶入對應的 Viewmodel
        /// </summary>
        /// <typeparam name="TViewModel">Model對應的ViewModel</typeparam>
        /// <returns></returns>
        List<TViewModel> GetListToViewModel<TViewModel>();

        /// <summary>
        /// 取得某一個條件下面的某一筆Entity並且轉成對應的ViewModel
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="wherePredicate">過濾邏輯</param>
        /// <returns>取得轉換過的ViewModel或者是null</returns>
        TViewModel GetSpecificDetailToViewModel<TViewModel>(Expression<Func<T, bool>> wherePredicate);

        /// <summary>
        /// 取得某一個條件下面的某一筆Entity並且轉成對應的List<ViewModel>
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="wherePredicate">過濾邏輯</param>
        /// <param name="wherePredicates">要取得的Where條件。可新增N個條件值</param>
        /// <returns>取得轉換過的ViewModel或者是null</returns>
        List<TViewModel> GetSpecificDetailToViewModel<TViewModel>(Expression<Func<T, bool>> wherePredicate,
            params Expression<Func<T, bool>>[] predicates);
    }
}

 

服務層-實作類 : 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using System.Linq.Expressions;
using AutoMapper;
using AutoMapper.Configuration;

namespace MVC_Service 
{
    //internal
    /// <summary>
    /// 外部若要操作資料應該都透過 Service層 做存取與操作 
    /// 不應當自己實例化出 EFGenercUnitOfWork && EFGenericRepository
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    internal class GenericService<TEntity> : Interface.IGenericService<TEntity>
        where TEntity : class
    {
        //宣告IUnitOfWork的屬性
        protected IUnitOfWork.Interface.IUnitOfWork db;

        public GenericService(DbContext inContext)
        {
            if (inContext == null) { throw new ArgumentNullException("context"); }
            this.db = new IUnitOfWork.EFGenercUnitOfWork(inContext);
        }


        #region AutoMapper
        // 使用 AutoMapper 轉成對應的 Entity Model
        /// <summary>
        /// 接收前端的ViewModel 產生對應的 Model 且帶入資料
        /// </summary>
        /// <typeparam name="ViewModel">對應的ViewModel類型</typeparam>
        /// <param name="inViewModelData">帶有資料的ViewModel</param>
        /// <returns>帶有資料的 TEntity 類別</returns>
        protected static TEntity staticConvertViewModelToModel<ViewModel>(ViewModel inViewModelData)
        {
            //建立組態檔
            var cfg = new MapperConfigurationExpression();
            //可反轉
            cfg.CreateMap<ViewModel, TEntity>().ReverseMap();
            //實例 Mapper 的物件
            Mapper.Initialize(cfg);

            // 依據 ViewModel 的資料 建立出 <TEntity> 型別的物件
            return Mapper.Map<TEntity>(inViewModelData);
        }

        // 使用 AutoMapper 轉成對應的 ViewModel
        /// <summary>
        /// 抓取出來的資料 轉成對應的ViewModel 並帶入資料
        /// </summary>
        /// <typeparam name="TEntity">對應的Model類型</typeparam>
        /// <param name="inModelData">帶有資料的ViewModel</param>
        /// <returns>帶有資料的 ViewModel</returns>
        protected static ViewModel staticConvertModelToViewModel<ViewModel>(TEntity inModelData)
        {
            //建立組態檔
            var cfg = new MapperConfigurationExpression();
            //可反轉
            cfg.CreateMap<TEntity, ViewModel>().ReverseMap();
            //實例 Mapper 的物件
            Mapper.Initialize(cfg);

            // 依據 ViewModel 的資料 建立出 <TEntity> 型別的物件
            return Mapper.Map<ViewModel>(inModelData);
        }

        // 使用 AutoMapper 轉成對應的 List<ViewModel>
        /// <summary>
        /// 抓取出來的 IQueryable<TEntity> 資料 轉成對應的 IQueryable<ViewModel> 並帶入資料
        /// </summary>
        /// <typeparam name="TEntity">對應的Model類型</typeparam>
        /// <param name="inModelData">帶有資料的ViewModel</param>
        /// <returns>帶有資料的 IQueryable<ViewModel> 集合</returns>
        protected static IQueryable<ViewModel> staticConvertListModelToListViewModel<ViewModel>(
            IQueryable<TEntity> inModelData)
        {
            //建立組態檔
            var cfg = new MapperConfigurationExpression();
            //可反轉
            cfg.CreateMap<TEntity, ViewModel>().ReverseMap();
            //實例 Mapper 的物件
            Mapper.Initialize(cfg);

            // 依據 ViewModel 的資料 建立出 <TEntity> 型別的物件
            return Mapper.Map<IQueryable<ViewModel>>(inModelData);
        }

        // 使用 AutoMapper 修改Model的資料
        ///// <summary>
        ///// 是將以撈取出來的EntityModel的資料 以 ViewModel 中的資料 修改 
        ///// </summary>
        ///// <typeparam name="ViewModel">對應的ViewModel類型</typeparam>
        ///// <param name="inViewModelData">帶有資料的 ViewModel</param>
        ///// <param name="inEntityModelData">欲更新的EntityModel</param>
        //protected static void staticUpdViewModelToEntityModel<ViewModel>(
        //    ViewModel inViewModelData, TEntity inEntityModelData)
        //{
        //    //建立組態檔
        //    var cfg = new MapperConfigurationExpression();
        //    //可反轉
        //    cfg.CreateMap<TEntity, ViewModel>().ReverseMap();
        //    //實例 Mapper 的物件
        //    Mapper.Initialize(cfg);
        //    //此時 AutoMap 將使用 ViewModel 修改 Model 的資料
        //    Mapper.Map(inViewModelData, inEntityModelData);
        //}
        #endregion

        /// <summary>
        /// 依照某一個ViewModel的值,產生對應的Entity並且新增到資料庫
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="viewModel">ViewModel的Reference</param>
        /// <returns>是否儲存成功</returns>
        /// <exception cref="System.ArgumentNullException">資料的Entity為Null</exception>
        public virtual void CreateViewModelToDatabase<TViewModel>(TViewModel viewModel)
        {
            if (viewModel == null) { throw new ArgumentNullException("instance"); }
            var entity = staticConvertViewModelToModel(viewModel);
            this.db.GetRepository<TEntity>().Create(entity);
            this.db.Save();
        }

        /// <summary>
        /// 刪除某一筆Entity
        /// </summary>
        /// <param name="wherePredicate">過濾出要被刪除的Entity條件</param>
        /// <returns>是否刪除成功</returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void Delete(Expression<Func<TEntity, bool>> wherePredicate)
        {
            var data = this.db.GetRepository<TEntity>().Read(wherePredicate);
            if (data == null) { throw new ArgumentNullException("Not Found"); }
            this.db.GetRepository<TEntity>().Delete(data);
            this.db.Save();
        }


        /// <summary>
        /// 依照某一個ViewModel的值,覆寫對應的Entity
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="viewModel">ViewModel的值</param>
        /// <returns>是否更新成功</returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void UpdateViewModelToDatabase<TViewModel>(TViewModel viewModel)
        {
            if (viewModel == null) { throw new ArgumentNullException("instance"); }
            //取得對應並帶有資料的 Entity 類別
            var enitity = staticConvertViewModelToModel(viewModel);
            this.db.GetRepository<TEntity>().Update(enitity);
            this.db.Save();
        }

        /// <summary>
        /// 更新一筆資料的內容。只更新部分欄位的。
        /// Lambda 運算式 只需要傳遞欄位屬性 EX : x => x.ColumnName1, x => x.Column2....
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="updateProperties">需要更新的欄位。</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public virtual void UpdateViewModelToDatabase<TViewModel>(
            TViewModel viewModel, Expression<Func<TEntity, object>>[] updateProperties)
        {
            if (viewModel == null) { throw new ArgumentNullException("instance"); }
            var entity = staticConvertViewModelToModel(viewModel);
            this.db.GetRepository<TEntity>().Update(entity, updateProperties);
            this.db.Save();
        }

        /// <summary>
        /// 取得全部的資料 且將資料帶入對應的 Viewmodel
        /// </summary>
        /// <typeparam name="TViewModel">Model對應的ViewModel</typeparam>
        /// <returns> List<TViewModel> 集合類別 </returns>
        public virtual List<TViewModel> GetListToViewModel<TViewModel>()
        {
            var data = this.db.GetRepository<TEntity>().Reads();
            return staticConvertListModelToListViewModel<TViewModel>(data).ToList();
        }

        /// <summary>
        /// 取得某一個條件下面的某一筆Entity並且轉成對應的ViewModel
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="wherePredicate">過濾邏輯</param>
        /// <returns>取得轉換過的ViewModel或者是null</returns>
        public virtual TViewModel GetSpecificDetailToViewModel<TViewModel>(Expression<Func<TEntity, bool>> wherePredicate)
        {
            var data = this.db.GetRepository<TEntity>().Read(wherePredicate);
            return staticConvertModelToViewModel<TViewModel>(data);
        }

        /// <summary>
        /// 取得某一個條件下面的某一筆Entity並且轉成對應的ViewModel
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel的形態</typeparam>
        /// <param name="wherePredicate">過濾邏輯</param>
        /// <param name="wherePredicates">要取得的Where條件。可新增N個條件值</param>
        /// <returns>取得轉換過的List<ViewModel>或者是null</returns>
        public virtual List<TViewModel> GetSpecificDetailToViewModel<TViewModel>(
            Expression<Func<TEntity, bool>> wherePredicate, params Expression<Func<TEntity, bool>>[] predicates)
        {
            var datas = this.db.GetRepository<TEntity>().Reads(wherePredicate, predicates);
            return staticConvertListModelToListViewModel<TViewModel>(datas).ToList();
        }

        #region IDisposable Support
        private bool _disposedvalue = false; // 偵測多餘的呼叫

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposedvalue)
            {
                if (disposing)
                {                    
                }
                this.db.Dispose();
                this.db = null;
                this._disposedvalue = true;
            }
        }

        ~GenericService()
        {
            // 請勿變更這個程式碼。請將清除程式碼放入上方的 Dispose(bool disposing) 中。
            Dispose(false);
        }

        // 加入這個程式碼的目的在正確實作可處置的模式。
        public void Dispose()
        {
            // 請勿變更這個程式碼。請將清除程式碼放入上方的 Dispose(bool disposing) 中。
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}

 

註記 : 未來修改的方向與資料存取層雷同

  • 服務層不應該針對<模型類別(Model class)>來產生實例
  • 服務層層應針對<DbContext>來產生實例

 


多多指教!! 歡迎交流!!

你不知道自己不知道,那你會以為你知道