netCore3.1專案搭建過程記錄-省的每次都傻乎乎的不知道應該先幹啥
十年河東,十年河西,莫欺少年窮
學無止境,精益求精
我搭建專案的習慣,一般先搭建專案整體的層次劃分,首先貼出我搭建專案的各個層次劃分:
專案分為8層,資料庫層採用EfCore 結合 sqlSugar的方式,從上到下依次為:公共類層、EfCore上下文層、SqlSugar上下文層,業務實體層、sqlSugar實體層【由工具生成】、介面層、服務層、web站點【webApi屬於這一層】,各層次之間的引用,我就不累述了。
清晰了專案的各層次,下面我們逐步完善這個裸體專案
1、搭建你的資料庫上下文層
1.1、搭建EfCore上下文
1、開啟程式包管理控制檯,選中EFCoreContext層,並依次執行如下控制命令
Install-Package Microsoft.EntityFrameworkCore Install-Package Microsoft.EntityFrameworkCore.SqlServer Install-Package Microsoft.EntityFrameworkCore.Tools Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design -Version 3.1.4
2、選中啟動專案webSite ,並依次執行上述控制命令
Install-Package Microsoft.EntityFrameworkCore Install-Package Microsoft.EntityFrameworkCore.SqlServer Install-Package Microsoft.EntityFrameworkCore.Tools Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design -Version 3.1.4
如下圖:
3、以上兩步驟執行完畢後,在程式包管理控制檯中繼續切換到EFCoreContext層,並執行如下指令生成資料庫上下文【注意,連線字串要改成你自己的】
4、執行成功後,生成的上下文如下:
我的資料庫指令碼請參考:
最後在啟動類中註冊SqlServer服務,如下:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); #region 註冊SQLSERVER services.AddDbContext<DbRoleManagerContext>(options => options.UseSqlServer(Configuration.GetConnectionString("WuAnDBContext"))); #endregion
這樣的話,EFCore的上下文就生成了,因為netCore對原生SQL的支援不是特別好,因此,我的專案中引入了sqlSugar作為支援。下面我們來搭建sqlSugar的上下文。
1.2、搭建sqlSugar上下文及sqlsugar實體
沒有玩過SqlSugar的小虎斑可以參考這篇部落格:SqlSugar+SqlServer+NetCore3.1 入門案例
SqlSugar的工具【可通過專案/工具生成】大家可參考:http://www.codeisbug.com/Doc/8/1123或者 直接去CSDN 上下載相關工具/專案:https://download.csdn.net/download/wolongbb/12997789
1、在專案SqlSugarContext、SqlSugarModel 中,通過NuGet安裝sqlSugar引用 和 SqlSugar的依賴項Newtonsoft.Json V 12.0.3及System.Data.SqlClient V 4.8.2 版本
2、通過下載的專案,生成sqlSugar上下文及sqlsugar實體
注意:在下載的專案SoEasyPlatform-master中需要自行修改連線字串及名稱空間。
我的sqlsugar上下文如下:
using SqlSugarModel.Enties; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace SugarContext { public class SugarDbContext { /// 獲取連線字串 private static string Connection = "Data Source=LAPTOP-P5GVS4UM;Initial Catalog=DbRoleManager;User ID=sa;Password=Aa123456"; public SugarDbContext() { Db = new SqlSugarClient(new ConnectionConfig() { ConnectionString = Connection, DbType = DbType.SqlServer, InitKeyType = InitKeyType.Attribute,//從特性讀取主鍵和自增列資訊 IsAutoCloseConnection = true,//開啟自動釋放模式和EF原理一樣我就不多解釋了 }); //調式程式碼 用來列印SQL Db.Aop.OnLogExecuting = (sql, pars) => { Console.WriteLine(sql + "\r\n" + Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); Console.WriteLine(); }; } //注意:不能寫成靜態的 public SqlSugarClient Db;//用來處理事務多表查詢和複雜的操作 } public class SugarDbContext<T> where T : class, new() { public SugarDbContext() { Db = new SqlSugarClient(new ConnectionConfig() { ConnectionString = "Data Source=LAPTOP-P5GVS4UM;Initial Catalog=DbRoleManager;User ID=sa;Password=Aa123456", DbType = DbType.SqlServer, InitKeyType = InitKeyType.Attribute,//從特性讀取主鍵和自增列資訊 IsAutoCloseConnection = true,//開啟自動釋放模式和EF原理一樣我就不多解釋了 }); //調式程式碼 用來列印SQL Db.Aop.OnLogExecuting = (sql, pars) => { Console.WriteLine(sql + "\r\n" + Db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value))); Console.WriteLine(); }; } //注意:不能寫成靜態的 public SqlSugarClient Db;//用來處理事務多表查詢和複雜的操作 public SimpleClient<T> CurrentDb { get { return new SimpleClient<T>(Db); } }//用來操作當前表的資料 public SimpleClient<User_Dept> User_DeptDb { get { return new SimpleClient<User_Dept>(Db); } }//用來處理User_Dept表的常用操作 public SimpleClient<User_MenuButton> User_MenuButtonDb { get { return new SimpleClient<User_MenuButton>(Db); } }//用來處理User_MenuButton表的常用操作 public SimpleClient<User_Role> User_RoleDb { get { return new SimpleClient<User_Role>(Db); } }//用來處理User_Role表的常用操作 public SimpleClient<User_Role_Dept> User_Role_DeptDb { get { return new SimpleClient<User_Role_Dept>(Db); } }//用來處理User_Role_Dept表的常用操作 public SimpleClient<User_Role_MenuButton> User_Role_MenuButtonDb { get { return new SimpleClient<User_Role_MenuButton>(Db); } }//用來處理User_Role_MenuButton表的常用操作 public SimpleClient<User_Account> User_AccountDb { get { return new SimpleClient<User_Account>(Db); } }//用來處理User_Account表的常用操作 public SimpleClient<User_Account_Dept> User_Account_DeptDb { get { return new SimpleClient<User_Account_Dept>(Db); } }//用來處理User_Account_Dept表的常用操作 public SimpleClient<User_Account_Role> User_Account_RoleDb { get { return new SimpleClient<User_Account_Role>(Db); } }//用來處理User_Account_Role表的常用操作 /// <summary> /// 獲取所有 /// </summary> /// <returns></returns> public virtual List<T> GetList() { return CurrentDb.GetList(); } /// <summary> /// 根據表示式查詢 /// </summary> /// <returns></returns> public virtual List<T> GetList(Expression<Func<T, bool>> whereExpression) { return CurrentDb.GetList(whereExpression); } /// <summary> /// 根據表示式查詢分頁 /// </summary> /// <returns></returns> public virtual List<T> GetPageList(Expression<Func<T, bool>> whereExpression, PageModel pageModel) { return CurrentDb.GetPageList(whereExpression, pageModel); } /// <summary> /// 根據表示式查詢分頁並排序 /// </summary> /// <param name="whereExpression">it</param> /// <param name="pageModel"></param> /// <param name="orderByExpression">it=>it.id或者it=>new{it.id,it.name}</param> /// <param name="orderByType">OrderByType.Desc</param> /// <returns></returns> public virtual List<T> GetPageList(Expression<Func<T, bool>> whereExpression, PageModel pageModel, Expression<Func<T, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) { return CurrentDb.GetPageList(whereExpression, pageModel, orderByExpression, orderByType); } /// <summary> /// 根據主鍵查詢 /// </summary> /// <returns></returns> public virtual T GetById(dynamic id) { return CurrentDb.GetById(id); } /// <summary> /// 根據主鍵刪除 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Delete(dynamic id) { return CurrentDb.Delete(id); } /// <summary> /// 根據實體刪除 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Delete(T data) { return CurrentDb.Delete(data); } /// <summary> /// 根據主鍵刪除 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Delete(dynamic[] ids) { return CurrentDb.AsDeleteable().In(ids).ExecuteCommand() > 0; } /// <summary> /// 根據表示式刪除 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Delete(Expression<Func<T, bool>> whereExpression) { return CurrentDb.Delete(whereExpression); } /// <summary> /// 根據實體更新,實體需要有主鍵 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Update(T obj) { return CurrentDb.Update(obj); } /// <summary> ///批量更新 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Update(List<T> objs) { return CurrentDb.UpdateRange(objs); } /// <summary> /// 插入 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Insert(T obj) { return CurrentDb.Insert(obj); } /// <summary> /// 批量 /// </summary> /// <param name="id"></param> /// <returns></returns> public virtual bool Insert(List<T> objs) { return CurrentDb.InsertRange(objs); } //自已擴充套件更多方法 } }View Code
這樣的話,sqlSugar上下文層及sqlsugar實體層就搭建完畢了,結果如下:
綜上所述,資料庫部分的搭建就完成了,有EFCore和sqlSugar,我們就可以玩轉資料庫的操作了。
2、搭建你的webSite層【Swagger,log4Net,Json web Token,異常捕獲中介軟體,Api路由等等】
2.1、搭建你的swagger並結合Jwt驗證,使你的Api更安全並易於管理及測試,更詳細內容可參考:NetCore3.1 如何新增帶有JWT Token 驗證的Swagger
1、開啟程式包管理控制檯,執行如下指令,安裝swagger相關引用
Install-Package Swashbuckle.AspNetCore -Version 5.0.0
2、安裝Swashbuckle.AspNetCore.Filters包 版本5.12
Install-Package Swashbuckle.AspNetCore.Filters -Version 5.1.2
3、Nuget安裝JwtBearer引用v 3.1.10版本
4、修改StartUp類如下:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using EFCoreContext.Models; using log4net; using log4net.Config; using log4net.Repository; using LogicModel; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.Filters; using WebSite.Middlewares; namespace WebSite { public class Startup { public static ILoggerRepository repository { get; set; } public Startup(IConfiguration configuration) { Configuration = configuration; // 指定配置檔案 repository = LogManager.CreateRepository("NETCoreRepository"); XmlConfigurator.Configure(repository, new FileInfo("Log4Net.config")); } 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.AddControllers(); #region 註冊SQLSERVER services.AddDbContext<DbRoleManagerContext>(options => options.UseSqlServer(Configuration.GetConnectionString("WuAnDBContext"))); #endregion #region JWT services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(x => { x.RequireHttpsMetadata = false; x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(TokenManagementModel.Secret)), ValidIssuer = TokenManagementModel.Issuer, ValidAudience = TokenManagementModel.Audience, ValidateIssuer = false, ValidateAudience = false }; }); #endregion #region 註冊Swagger服務 // 註冊Swagger服務 services.AddSwaggerGen(c => { // 新增文件資訊 c.SwaggerDoc("v1", new OpenApiInfo { Title = "許可權管理相關介面", Version = "V1" }); //c.SwaggerDoc("demo", new OpenApiInfo { Title = "示例介面", Version = "demo" }); c.DocInclusionPredicate((docName, apiDesc) => apiDesc.GroupName == docName.ToUpper()); var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//獲取應用程式所在目錄(絕對,不受工作目錄影響,建議採用此方法獲取路徑) var xmlPath = Path.Combine(basePath, "WebSite.xml"); c.IncludeXmlComments(xmlPath); #region Jwt //開啟許可權小鎖 c.OperationFilter<AddResponseHeadersFilter>(); c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>(); //在header中新增token,傳遞到後臺 c.OperationFilter<SecurityRequirementsOperationFilter>(); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT授權(資料將在請求頭中進行傳遞)直接在下面框中輸入Bearer {token}(注意兩者之間是一個空格) \"", Name = "Authorization",//jwt預設的引數名稱 In = ParameterLocation.Header,//jwt預設存放Authorization資訊的位置(請求頭中) Type = SecuritySchemeType.ApiKey }); #endregion }); #endregion } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } //允許跨域 app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); #region 啟用Swagger中介軟體 // 啟用Swagger中介軟體 app.UseSwagger(c => c.RouteTemplate = "swagger/{documentName}/swagger.json"); // 配置SwaggerUI app.UseSwaggerUI(c => { c.SwaggerEndpoint($"/swagger/v1/swagger.json", "V1"); }); #endregion //註冊異常中介軟體 app.UseMiddleware<ExceptionMiddlewares>(); app.UseAuthentication(); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }View Code
Jwt金鑰類:
/// <summary> /// POCO類,用來儲存簽發或者驗證jwt時用到的資訊 /// </summary> public class TokenManagementModel { public static string Secret = "987654321987654321";//私鑰 public static string Issuer = "webapi.cn"; public static string Audience = "WebApi"; public static int AccessExpiration = 180;//過期時間 public static int RefreshExpiration = 180;//重新整理時間 }View Code
4、由於這塊內容在NetCore3.1 如何新增帶有JWT Token 驗證的Swagger中講解比較詳細,就不一步步演示了。
2.2、搭建你的Log4Net日誌並結合異常處理中介軟體,使你的系統輕而易舉的處理系統錯誤及異常,這塊內容可參考:NetCore 異常處理過濾器、中介軟體 、並整合Log4Net
1、首選通過Nuget引用Log4Net,如下:
然後在sutartUp的建構函式中註冊Log4Net
public static ILoggerRepository repository { get; set; } public Startup(IConfiguration configuration) { Configuration = configuration; // 指定配置檔案 repository = LogManager.CreateRepository("NETCoreRepository"); XmlConfigurator.Configure(repository, new FileInfo("Log4Net.config")); }
2、新增Log4Net日誌的XML配置檔案Log4Net.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <!-- This section contains the log4net configuration settings --> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" /> </appender> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="Log\\LogInfo\\" /> <appendToFile value="true" /> <rollingStyle value="Composite" /> <staticLogFileName value="false" /> <datePattern value="yyyyMMdd'.log'" /> <maxSizeRollBackups value="10" /> <maximumFileSize value="5MB" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%n異常時間:%d [%t] %n異常級別:%-5p 
異 常 類:%c [%x] %n%m %n" /> </layout> </appender> <!-- Setup the root category, add the appenders and set the default level --> <root> <level value="ALL" /> <appender-ref ref="ConsoleAppender" /> <appender-ref ref="FileAppender" /> <appender-ref ref="RollingLogFileAppender" /> </root> </log4net> </configuration>
截止到這兒,Log4Net就成功引入到專案中了。
3、在專案中新增異常處理中介軟體:ExceptionMiddlewares,結合Log4Net進行異常日誌記錄
using log4net; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace WebSite.Middlewares { public class ExceptionMiddlewares { private ILog log; private readonly RequestDelegate next; private IHostingEnvironment environment; public ExceptionMiddlewares(RequestDelegate next, IHostingEnvironment environment, ILogger<ExceptionMiddlewares> logger) { this.log = LogManager.GetLogger(Startup.repository.Name, typeof(ExceptionMiddlewares)); this.next = next; this.environment = environment; } public async Task Invoke(HttpContext context) { try { await next.Invoke(context); var features = context.Features; } catch (Exception e) { await HandleException(context, e); } } private async Task HandleException(HttpContext context, Exception e) { context.Response.StatusCode = 500; context.Response.ContentType = "text/json;charset=utf-8;"; string error = ""; if (environment.IsDevelopment()) { var json = new { message = e.Message + "【" + e.StackTrace + "】" }; log.Error(json); error = JsonConvert.SerializeObject(json); } else { var json = new { message = e.Message + "【" + e.StackTrace + "】" }; log.Error(json); error = JsonConvert.SerializeObject(json); error = "抱歉,出錯了"; } await context.Response.WriteAsync(error); } } }View Code
4、在startup類中註冊異常中介軟體
//註冊異常中介軟體 app.UseMiddleware<ExceptionMiddlewares>();
5、這塊內容已詳細實現,可參考:NetCore 異常處理過濾器、中介軟體 、並整合Log4Net,這兒不再重複累述。
做個測試,寫個被除數為0 的程式,看看中介軟體能否捕獲異常,Log4Net能否記錄下異常資訊
Log4Net記錄的日誌如下:
3、繼續完善你搭建的專案【新增選項模式,封裝分頁類,統一返回值BaseResponse,MongoDB/Redis/MQ/快取/Jwt生成、驗證,等等】
寫到這兒,專案的基本搭建就完成了,關於:新增選項模式,封裝分頁類,統一返回值BaseResponse,MongoDB/Redis/MQ/快取/Jwt生成、驗證等等,有興趣的可以搜尋我的部落格,基本都有實現。
可參考:
NetCore讀取配置檔案,簡單實現。
NetCore3.1 使用 mongoDb 儲存日誌,提升查詢效率
訊息佇列 RocketMQ 併發量十萬級
Redis 的基礎資料型別
高併發時,使用Redis應注意的問題 及 Redis快取幫助類
EFcoe中如果通過日誌記錄Linq轉化的SQL語句
EFCore 封裝分頁功能
ASP.NET Core 中的響應快取 / ResponseCache
實時web應用方案——SignalR(.net core) 理論篇
NetCore 配置檔案---直接讀取及選項模式讀取
NetCore 基於identity的登入驗證授權機制
深入理解 NetCore 中的依賴注入的好處 及 、Singleton、Scoped、Transient 三種物件的差異
asp.net core 系列 5 專案實戰之:NetCore 的 async 和 await(參考自:Microsoft教程)
SqlSugar 用法大全
總結:
一個優秀的程式猿,不僅僅要會用框架,也應該會自己搭建一個框架,想成為架構師,這只是開始。
@天才臥龍的部落格