最近幾年先後端分離架構大行其道,並且各類框架也是層出不窮。本文經過dotnetcore +vue 來介紹 先後端分離架構實戰。html
http://www3.ntu.edu.sg/home/ehchua/programming/sql/SampleDatabases.html前端
總體上SPA 結構vue
漸進式 JavaScript 框架.
三大特色:易用,靈活, 高效。
詳細信息見 https://cn.vuejs.org/node
網站快速成型工具
Element,一套爲開發者、設計師和產品經理準備的基於 Vue 2.0 的桌面端組件庫
詳細信息見 http://element-cn.eleme.io/#/zh-CNmysql
服務端代碼地址:https://github.com/hbb0b0/Hbb0b0.CMS/tree/master/Hbb0b0.CMS.WebAPIios
摘取部分類予以說明。git
通用工具的封裝github
配置工具類vue-router
DB配置sql
Cors配置
WebApi配置
DB工具類
DapperContext
IRespository
ResponsitoryBase
包含獲取列表,獲取分頁列表,增刪改的通用操做的封裝
SimpleCRUD
github上對Dapper的進一步分裝
消息
數據轉換
entity --> Dto
DTObase
PageList
返回給前端的數據對象,隔離DB model變化直接映射前端對象,使View與Model解耦。
DB 中的表映射到 Model
DB 操做接口
DB 操做接口的實現
Serive接口,能夠供給第三方調用,也是第三方調用的規範,同時也約束着服務實現。
目前提供給API層調用。
Serive接口的實現
以WebAPI 形式提供給前臺調用,並非嚴格按照 WebApi 規範 post,get,put,delete 調用.使用了 core中的 mvc 方式。
{ "Logging": { "IncludeScopes": false, "Debug": { "LogLevel": { "Default": "Information" } }, "Console": { "LogLevel": { "Default": "Information" } } }, "WebAPI": { "DB": { "ConnectionString": "server=localhost;database=employees;uid=root;pwd=sqlsa;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True;", "ReadingConnectionString": "server=localhost;database=employees;uid=root;pwd=sqlsa;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True;", "WritingConnectionString": "server=localhost;database=employees;uid=root;pwd=sqlsa;charset='utf8';persistsecurityinfo=True;SslMode=none;Allow User Variables=True;" }, "Cors": { "Name": "CMSCorsConfig", "Original": "http://localhost:8080" } } }
API
Startup
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CMS.Common; using CMS.Common.Config; using CMS.Common.DB; using CMS.IRepository; using CMS.IService; using CMS.Repository; using CMS.Service; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace CMS.WebApi { 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() //序列化設置 .AddJsonOptions(options => { //忽略循環引用 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //不使用駝峯樣式的key //options.SerializerSettings.ContractResolver = new DefaultContractResolver(); //設置時間格式 options.SerializerSettings.DateFormatString = "yyyy-MM-dd hh:mm:ss"; } ); services.AddOptions(); //讀取配置 services.Configure<WebApiOption>(Configuration.GetSection("WebAPI")); IServiceProvider serviceProvider = services.BuildServiceProvider(); //獲取配置 WebApiOption config = serviceProvider.GetService<IOptions<WebApiOption>>().Value; //Cors配置 AddCorsService(services,config); //DB Service AddDBService(services,config); } /// <summary> /// AddDBService /// </summary> /// <param name="services"></param> /// <param name="config"></param> private void AddDBService(IServiceCollection services,WebApiOption config) { //設置全局配置 services.AddSingleton<IDapperContext>(_ => new DapperContext( config )); services.AddScoped<IDepartmentRep, DepartmentRep>(); services.AddScoped<IDepartmentService, DepartmentService>(); services.AddScoped<IEmployeeRep, EmployeeRep>(); services.AddScoped<IEmployeeService, EmployeeService>(); services.AddScoped<ITitleRep, TitleRep>(); services.AddScoped<ISalaryRep, SalaryRep>(); } /// <summary> /// AddCorsService /// </summary> /// <param name="services"></param> /// <param name="config"></param> private void AddCorsService(IServiceCollection services, WebApiOption config) { //添加cors 服務 services.AddCors(options => options.AddPolicy(WebApiOption.CORS_POLICY_NAME, p => p.WithOrigins(config.Cors.Original) .AllowAnyMethod().AllowAnyHeader())); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{no?}", defaults: new { controller = "Home", action = "Index" }); }); //配置Cors app.UseCors(WebApiOption.CORS_POLICY_NAME); } } }
using CMS.Common.Config; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace CMS.WebApi.Common { [Route("api/[controller]")] [EnableCors(WebApiOption.CORS_POLICY_NAME)] public abstract class BaseController: Controller { protected readonly ILogger m_Logger; public BaseController(ILogger<BaseController> logger) { m_Logger = logger; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CMS.Common; using CMS.Common.Config; using CMS.Common.Message; using CMS.DTO; using CMS.Entity; using CMS.IService; using CMS.WebApi.Common; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; namespace CMS.WebApi.Controllers { public class EmployeeController : BaseController { private DBOption m_CmsOptions; private IEmployeeService m_Service; public EmployeeController(IOptions<DBOption> option, IEmployeeService employeeService, ILogger<EmployeeController> logger) : base(logger) { this.m_CmsOptions = option.Value; m_Service = employeeService; } [Route("[action]/{pageNumber}/{rowsPerPage}")] [HttpGet] public ReponseMessage<PagedList<EmployeeDTO>> GetPagedList(int pageNumber, int rowsPerPage) { var result = m_Service.GetPagedList(pageNumber, rowsPerPage); return m_Service.GetPagedList(pageNumber, rowsPerPage); } [Route("[action]")] [HttpPost] public ReponseMessage<PagedList<EmployeeDTO>> Query([FromBody]QueryCondition<EmployeeQuery> condition) { var result = m_Service.Query(condition); m_Logger.LogInformation("GetPagedList:{0}", JsonConvert.SerializeObject(result)); return result; } [Route("[action]/{no}")] [HttpGet] public ReponseMessage<EmployeeDetailDto> GetDetail(string no) { var result = m_Service.GetDetail(no); return result; } [Route("[action]")] [HttpPost] public ReponseMessage<int> Add([FromBody]EmployeeDTO dto) { var result = m_Service.Add(dto); return result; } [Route("[action]/{empNo}")] [HttpPost] public ReponseMessage<int> Delete(int empNo) { var result = m_Service.Delete(empNo); return result; } [Route("[action]/")] [HttpPost] public ReponseMessage<int> Update([FromBody]EmployeeDTO dto) { var result = m_Service.Update(dto); return result; } } }
using CMS.Common; using CMS.Common.Message; using CMS.DTO; using CMS.Entity; using CMS.Model; using System; using System.Collections.Generic; namespace CMS.IService { public interface IEmployeeService { ReponseMessage<PagedList<EmployeeDTO>> GetPagedList(int pageNumber, int rowsPerPage); ReponseMessage<PagedList<EmployeeDTO>> Query(QueryCondition<EmployeeQuery> condition); ReponseMessage<EmployeeDetailDto> GetDetail(string emp_no); ReponseMessage<int> Add(EmployeeDTO dto); ReponseMessage<int> Update(EmployeeDTO dto); ReponseMessage<int> Delete(int emp_No); } }
using CMS.Common; using CMS.Common.Message; using CMS.DTO; using CMS.Entity; using CMS.IRepository; using CMS.IService; using CMS.Model; using CMS.Utility; using Dapper; using System; using System.Collections.Generic; using System.Linq; namespace CMS.Service { public class EmployeeService : IEmployeeService { private IEmployeeRep m_Rep; private ITitleRep m_TitleRep; private ISalaryRep m_SalaryRep; public EmployeeService(IEmployeeRep rep,ITitleRep titleRep, ISalaryRep salaryRep) { m_Rep = rep; m_TitleRep = titleRep; m_SalaryRep = salaryRep; } public ReponseMessage<EmployeeDetailDto> GetDetail(string emp_no) { ReponseMessage<EmployeeDetailDto> result = new ReponseMessage<EmployeeDetailDto>(); EmployeeDetailDto data = new EmployeeDetailDto(); var emp = m_Rep.Get(emp_no); data.Info = emp.JTransformTo<EmployeeDTO>(); var titleList = m_TitleRep.GetList(string.Format($"where emp_no = {emp_no}")); var salaryList = m_SalaryRep.GetList(string.Format($"where emp_no = {emp_no} order by from_date ")); result.Data = data; result.IsSuccess = true; if (titleList != null) { result.Data.TitleList = titleList.JTransformTo<TitleDto>(); } if (salaryList!=null) { result.Data.SalaryList = salaryList.JTransformTo<SalaryDto>(); } return result; } public ReponseMessage<PagedList<EmployeeDTO>> GetPagedList(int pageNumber, int rowsPerPage) { int total = 0; ReponseMessage<PagedList<EmployeeDTO>> result = new ReponseMessage<PagedList<EmployeeDTO>>(); result.Data = new PagedList<EmployeeDTO>(); var modelResult= m_Rep.GetPagedList(pageNumber, rowsPerPage, null, null, out total); result.Data.TotalCount = total; result.Data.Items=modelResult.Items.JTransformTo<EmployeeDTO>(); result.IsSuccess = true; return result; } public ReponseMessage<PagedList<EmployeeDTO>> Query(QueryCondition<EmployeeQuery> condition) { ReponseMessage<PagedList<EmployeeDTO>> result = new ReponseMessage<PagedList<EmployeeDTO>>(); result.Data = new PagedList<EmployeeDTO>(); var modelResult = m_Rep.Query(condition); result.Data.TotalCount = modelResult.TotalCount; if (modelResult.TotalCount > 0) { result.Data.Items = modelResult.TotalCount>0 ? modelResult.Items.JTransformTo<EmployeeDTO>():new List<EmployeeDTO>(); } result.IsSuccess = true; return result; } public ReponseMessage<int> Add(EmployeeDTO dto) { ReponseMessage<int> result = new ReponseMessage<int>(); if (dto==null ) { result.IsSuccess = false; result.MessageInfo = new MessageInfo() { ErrorCode = -1, Message ="無效的參數" }; } EmployeeModel entitity = dto.JTransformTo<EmployeeModel>(); int? opResult = m_Rep.CustomerAdd(entitity); if(opResult.HasValue && opResult>0) { result.IsSuccess = true; result.Data = opResult.Value; } return result; } public ReponseMessage<int> Update(EmployeeDTO dto) { ReponseMessage<int> result = new ReponseMessage<int>(); if (dto == null) { result.IsSuccess = false; result.MessageInfo = new MessageInfo() { ErrorCode = -1, Message = "無效的參數" }; } EmployeeModel entitity = dto.JTransformTo<EmployeeModel>(); int? opResult = m_Rep.Update(entitity); if (opResult.HasValue && opResult > 0) { result.IsSuccess = true; result.Data = opResult.Value; } return result; } public ReponseMessage<int> Delete(int empNo) { ReponseMessage<int> result = new ReponseMessage<int>(); int? opResult = m_Rep.Delete(empNo); if (opResult.HasValue && opResult ==1) { result.IsSuccess = true; result.Data = opResult.Value; } return result; } } }
using CMS.Common; using CMS.Common.DB; using CMS.DTO; using CMS.Entity; using CMS.Model; using System; using System.Collections.Generic; using System.Text; namespace CMS.IRepository { public interface IEmployeeRep: IRepository<EmployeeModel> { PagedList<EmployeeDTO> Query(QueryCondition<EmployeeQuery> query); int? Add(EmployeeModel entity); int? CustomerAdd(EmployeeModel entity); int? Delete(int empNo); int? Update(EmployeeModel entity); } }
-Responsitory 實現
using CMS.Common;
using CMS.Common.DB;
using CMS.DTO;
using CMS.IRepository;
using CMS.Model;
using System;
using System.Collections.Generic;
using System.Text;
using Dapper;
namespace CMS.Repository
{
public class EmployeeRep : ReponsitoryBase
{
private static object m_sync_Object = new object();
public EmployeeRep(IDapperContext dapper) : base(dapper)
{
} public int? Add(EmployeeModel entity) { return this.Insert(entity); } new public int? Update(EmployeeModel entity) { return base.Update(entity); } public int? CustomerAdd(EmployeeModel entity) { int cmdResult = -1; using (var connection = this.GetWritingConnection()) { var sql = new StringBuilder(" set @maxNo=(select max(emp_no)+1 from employees) ;"); sql.AppendLine("insert into employees(emp_no,first_name,last_name,gender,birth_date,hire_date) values(@maxNo,@first_name,@last_name,@gender,@birth_date,@hire_date); "); sql.AppendLine("select @maxNo;"); cmdResult = connection.ExecuteScalar<int>(sql.ToString(), entity); } return cmdResult; } public int? Delete(int empNo) { int cmdResult = -1; using (var connection = this.GetWritingConnection()) { cmdResult = connection.Execute("delete from employees where emp_no = @emp_no ;", new { emp_no = empNo }); } return cmdResult; } public PagedList<EmployeeDTO> Query(QueryCondition<EmployeeQuery> query) { PagedList<EmployeeDTO> pagedList = new PagedList<EmployeeDTO>(); #region sql var sql = new StringBuilder("SELECT SQL_CALC_FOUND_ROWS * from employees "); #endregion sql.AppendLine(" Where 1=1"); if (!string.IsNullOrEmpty(query.Param.First_Name)) { sql.AppendLine(string.Format(" and First_Name like '{0}'", query.GetLikeValue(query.Param.First_Name))); } if (!string.IsNullOrEmpty(query.Param.Last_Name)) { sql.AppendLine(string.Format(" and last_Name like '{0}'", query.GetLikeValue(query.Param.Last_Name))); } if (!string.IsNullOrEmpty(query.Param.Emp_No)) { sql.AppendLine(string.Format(" and emp_no = @Emp_No")); } if (!string.IsNullOrEmpty(query.Param.Gender)) { sql.AppendLine(string.Format(" and gender = @Gender")); } DateTime? hire_date_start = null; DateTime? hire_date_end = null; if (query.Param.Hire_Date_Range != null) { if (query.Param.Hire_Date_Range[0].HasValue) { hire_date_start = query.Param.Hire_Date_Range[0]; sql.AppendLine(string.Format(" and hire_date >= @Hire_Date_Range_Start")); } if (query.Param.Hire_Date_Range[1].HasValue) { hire_date_end = query.Param.Hire_Date_Range[1]; sql.AppendLine(string.Format(" and hire_date <= @Hire_Date_Range_End")); } } DateTime? birth_date_start = null; DateTime? birth_date_end = null; if (query.Param.Birth_Date_Range != null) { if (query.Param.Birth_Date_Range[0].HasValue) { birth_date_start = query.Param.Birth_Date_Range[0]; sql.AppendLine(string.Format(" and birth_date >= @Birth_Date_Range_Start")); } if (query.Param.Birth_Date_Range[1].HasValue) { birth_date_end = query.Param.Birth_Date_Range[1]; sql.AppendLine(string.Format(" and birth_date <= @Birth_Date_Range_End")); } } sql.AppendLine(" order by emp_no desc"); sql.AppendLine($" LIMIT {(query.pageInfo.PageIndex - 1) * query.pageInfo.PageSize},{query.pageInfo.PageSize}"); sql.Append(";"); using (var connection = this.GetReadingConnection()) { var result = connection.Query<EmployeeDTO>(sql.ToString(), new { Emp_No = query.Param.Emp_No, Hire_Date_Range_Start = hire_date_start, Hire_Date_Range_End = hire_date_end, Birth_Date_Range_Start = birth_date_start, Birth_Date_Range_End = birth_date_end, Gender = query.Param.Gender }).AsList(); pagedList.Items = result; pagedList.TotalCount = connection.ExecuteScalar<int>("SELECT FOUND_ROWS();"); } return pagedList; } }
}
目前測試 empoyee 30萬條分頁數據大體在400ms左右