[ASP.NET Core]ASP.net Core API 分層設計

架構系列

會寫這一篇,是要讓PG知道怎麼要建立API的基本雛形與分層架構,首先類別庫選擇用.NET Standard 2.0


在Repository專案裡面當中加入Nuget
1.Microsoft.EntityFrameworkCore
2.Microsoft.EntityFrameworkCore.SqlServer

下載完開始撰寫DbContext部分(這邊請參考MSDN Code First)部分

定義Employees

    public partial class Employees
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public string Designation { get; set; }

        public int Salary { get; set; }

        public string MobileNumber { get; set; }

    }
    public class DemoContext:DbContext
    {
        protected DemoContext()
        {
        }

        public DemoContext(DbContextOptions options) : base(options)
        {

        }

        public virtual DbSet<Employees> Employees { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(@"Server=localhost;Database=DemoDB;Integrated Security=True;");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Employees>(entity =>
            {
                entity.Property(e => e.Designation)
                    .IsRequired()
                    .HasMaxLength(100);

                entity.Property(e => e.MobileNumber)
                    .IsRequired()
                    .HasMaxLength(10);

                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(100);
            });
        }

    }

分別定義IGenericRepository.cs,IUnitOfWorks.cs

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

namespace DemoRepository.Interface
{
    public interface IGenericRepository<TEntity> where TEntity:class
    {
        IQueryable<TEntity> Entity();
        IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            Expression<Func<TEntity, object>>[] includeProperties =null);
        TEntity GetById(object Id);
        void Insert(TEntity entity);
        void Delete(object Id);
        void Delete(TEntity entityToDelete);
        void Update(TEntity entityToUpdate);
    }
}
using System;
using System.Collections.Generic;
using System.Text;
using DemoRepository.DataModels;

namespace DemoRepository.Interface
{
    public interface IUnitOfWorks
    {
        IGenericRepository<Employees> EmployeeRepository { get;  }

        void SaveChanges();
    }
}

開始撰寫公用的GenericRepository

using DemoRepository.DataModels;
using DemoRepository.Interface;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace DemoRepository
{
    public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
    {
        private readonly DemoContext _context;
        private DbSet<TEntity> _dbSet;

        public GenericRepository(DemoContext context)
        {
            _context = context;
            _dbSet = context.Set<TEntity>();
        }

        public IQueryable<TEntity> Entity()
        {
            return _dbSet;
        }

        public IEnumerable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            Expression<Func<TEntity, Object>>[] includeProperties = null)
        {
            IQueryable<TEntity> query = _dbSet;
            if (filter != null)
            {
                query = query.Where(filter);
            }
            if (includeProperties != null)
            {
                foreach (var property in includeProperties)
                {
                    query = query.Include(property);
                }
            }
            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

        public TEntity GetById(object id)
        {
            return _dbSet.Find(id);
        }

        public void Insert(TEntity entity)
        {
            _dbSet.Add(entity);
        }

        public void Delete(object id)
        {
            TEntity entityToDelete = _dbSet.Find(id);
            Delete(entityToDelete);
        }

        public void Delete(TEntity entityToDelete)
        {
            if (_context.Entry(entityToDelete).State == EntityState.Deleted)
            {
                _dbSet.Attach(entityToDelete);
            }
            _dbSet.Remove(entityToDelete);
        }

        public void Update(TEntity entityToUpdate)
        {
            _dbSet.Attach(entityToUpdate);
            _context.Entry(entityToUpdate).State = EntityState.Modified;
        }
    }
}

撰寫UnitOfWorks.cs
Unit Of Work:他是企業架構設計模式裡面的常見的Pattern、專門在處理一連串物件因交易受到影響,將物件
異動寫入之餘處理同步問題,主要用於處理Commit or RollBack
用途:
1.Transaction Manager 
2.DB Create/Update/Delete
3.預防重複Modify

using System;
using System.Collections.Generic;
using System.Text;
using DemoRepository.DataModels;
using DemoRepository.Interface;

namespace DemoRepository
{
    public class UnitOfWork:IUnitOfWorks
    {
        private DemoContext _context;
        private GenericRepository<Employees> _employeeRepository;
     
        public UnitOfWork(DemoContext context)
        {
            _context = context;
        }

        public IGenericRepository<Employees> EmployeeRepository
        {
            get
            {
                if (this._employeeRepository == null)
                {
                    this._employeeRepository = new GenericRepository<Employees>(_context);
                }
                return _employeeRepository;
            }
        }

        public void SaveChanges()
        {
            _context.SaveChanges();
        }
    }
}

接者定義DemoDTO專案
Nuget部分
1.System.ComponentModel.Annotations

using System;
using System.Security.Principal;
using  System.ComponentModel.DataAnnotations;
namespace DemoDTO
{
    public class EmployeeDTO
    {
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        [Required]
        public string Designation { get; set; }

        [Required]
        public int Salary { get; set; }

        [Required]
        [StringLength(10,MinimumLength = 10)]
        public string MobileNumber { get; set; }
    }
}

Service 專案Nuget部分要安裝AutoMapper
Service Layer部分定義

using System;
using System.Collections.Generic;
using System.Text;
using DemoDTO;

namespace DemoService.Interface
{
    public interface IEmployeeService
    {
        List<EmployeeDTO> GetEmployee();

        EmployeeDTO GetEmployeeId(int id);

        void CreateEmployee(EmployeeDTO employee);

        void UpdateEmployee(EmployeeDTO employee);

        void DeleteEmployee(int id);

    }
}


開始撰寫EmployeeService

using AutoMapper;
using DemoDTO;
using DemoRepository.DataModels;
using DemoRepository.Interface;
using DemoService.Interface;
using System.Collections.Generic;
using System.Linq;

namespace DemoService
{
    public class EmployeeService : IEmployeeService
    {
        private readonly IUnitOfWorks _unitOfWork;
        private readonly IMapper _mapper;

        public EmployeeService(IUnitOfWorks unitOfWork, IMapper mapper)
        {
            _unitOfWork = unitOfWork;
            _mapper = mapper;
        }

        public List<EmployeeDTO> GetEmployee()
        {
            var employeeList = _unitOfWork.EmployeeRepository.Get().ToList();
            return _mapper.Map<List<EmployeeDTO>>(employeeList);
        }

        public EmployeeDTO GetEmployeeId(int id)
        {
            var employeeEntity = _unitOfWork.EmployeeRepository.Get(item => item.Id == id).FirstOrDefault();
            return _mapper.Map<EmployeeDTO>(employeeEntity);
        }


        public void CreateEmployee(EmployeeDTO employee)
        {
            var employeeEntity = _mapper.Map<Employees>(employee);
            _unitOfWork.EmployeeRepository.Insert(employeeEntity);
            _unitOfWork.SaveChanges();
        }

        public void UpdateEmployee(EmployeeDTO employee)
        {
            var employeeEntity = _mapper.Map<Employees>(employee);
            _unitOfWork.EmployeeRepository.Update(employeeEntity);
            _unitOfWork.SaveChanges();
        }

        public void DeleteEmployee(int id)
        {
            var employeeEntity = _unitOfWork.EmployeeRepository.Get(item => item.Id == id).FirstOrDefault();
            _unitOfWork.EmployeeRepository.Delete(employeeEntity);
            _unitOfWork.SaveChanges();
        }
    }
}

開始定義Service Layer AutoMapper

using AutoMapper;
using DemoDTO;
using DemoRepository.DataModels;

namespace DemoService.AutoMapper
{
    public class DBToDomainProfile:Profile
    {
        public DBToDomainProfile()
        {
            CreateMap<Employees, EmployeeDTO>();
        }

    }
}
using AutoMapper;
using DemoDTO;
using DemoRepository.DataModels;

namespace DemoService.AutoMapper
{
    public class DomainToDBProfile : Profile
    {
        public DomainToDBProfile()
        {
            CreateMap<EmployeeDTO, Employees>();
        }

    }
}

API Demo
定義基本的Reponse建設

撰寫BaseReponse.cs

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

namespace Demo.Infrastructure
{
    public class BaseReponse
    {
        public bool Success { get; set; }
        public string Message { get; set; }

        public List<string> ValidationErrors { get; set; }
    }
}

定義ApiReponse

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

namespace Demo.Infrastructure
{
    public class ApiReponse<T>:BaseReponse where T : class
    {
        public ApiReponse()
        {
        }

        public ApiReponse(T data, bool success = true)
        {
            this.Success = success;
            this.Data = data;
        }
      
        public ApiReponse(string meeesge,T data, bool success = true)
        {
            this.Success = success;
            this.Data = data;
            this.Message = meeesge;
        }
      
        public T Data { get; set; }
    }
}

定義appsettings.json 加入DbConnection
Demo專案則分別要nuget
1.AutoMapper
2.AutoMapper.Extensions.Microsoft.DependencyInjection

撰寫一下EmployeeController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Demo.Infrastructure;
using DemoDTO;
using DemoService.Interface;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Demo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IEmployeeService _employeeService;

        public EmployeeController(IEmployeeService employeeService)
        {
            _employeeService = employeeService;
        }

        [HttpGet]
        public ApiReponse<List<EmployeeDTO>> List()
        {
            var employeeList = _employeeService.GetEmployee();
            return  new ApiReponse<List<EmployeeDTO>>(employeeList);
        }

        [HttpPost]
        [Route("create")]
        public BaseReponse Post([FromBody]EmployeeDTO employee)
        {
            _employeeService.CreateEmployee(employee);
            return new BaseReponse { Success = true };
        }

        [HttpPost]
        [Route("update")]
        public BaseReponse Update([FromBody]EmployeeDTO employee)
        {
            _employeeService.UpdateEmployee(employee);
            return new BaseReponse { Success = true };
        }


        [HttpDelete("{id}")]
        public void Delete(int id)
        {
            _employeeService.DeleteEmployee(id);
        }

    }
}


開始注入

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DemoRepository;
using DemoRepository.DataModels;
using DemoRepository.Interface;
using DemoService;
using DemoService.Interface;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using AutoMapper;
namespace Demo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            //開始注入
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
            services.AddDbContext<DemoContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddScoped<IUnitOfWorks, UnitOfWork>();
            services.AddScoped<IEmployeeService, EmployeeService>();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env,DemoContext context)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //建立db table
            context.Database.EnsureCreated();
            app.UseMvc();
        }
    }
}

開始測試Controller撈資料看看


 
請各位參考上面的作法,隨便找一張TABLE來實作一次分層架構玩玩看吧

元哥的筆記