• 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 + "
    " +
                        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 + "
    " +
                        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 &#xD;&#xA;异 常 类:%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 用法大全

    总结:

    一个优秀的程序猿,不仅仅要会用框架,也应该会自己搭建一个框架,想成为架构师,这只是开始。

    @天才卧龙的博客

  • 相关阅读:
    英语----非谓语动词to do
    英语----非谓语动词done
    英语----三大从句的混合实战
    英语----状语从句连接词(下)
    英语----状语从句连接词(中)
    英语----状语从句连接词(上)
    xcode7.3遇到的问题
    UIScrollView AutoLayout
    cocoapods 卸载  &  安装
    iOS即时通讯之CocoaAsyncSocket源码解析五
  • 原文地址:https://www.cnblogs.com/chenwolong/p/14110214.html
Copyright © 2020-2023  润新知