开发框架Furion之WebApi+SqlSugar (一)

目录

1.开发环境

2.项目创建

2.1创建WebApi主项目

2.2 创建Start类库

2.3创建Model实体类库

2.4创建Application仓储业务类库

2.5创建Unility通用方法类库

3.基础功能配置

3.1 Model实体对象与数据表映射

3.2 基类仓储及动态Api接口配置

3.3 数据库IOC注册

 3.4 Startup配置

3.5 Swagger配置

3.6启动配置

4.项目使用示例展示


1.开发环境

.NET6

Visual Studio 2022

SQLServer

SqlSugar SqlSugar ORM 5.X 官网 、文档、教程 - SqlSugar 5x - .NET果糖网

2.项目创建

2.1创建WebApi主项目

创建名为MyFurion.WebApi的项目

开发框架Furion之WebApi+SqlSugar (一)_第1张图片

 开发框架Furion之WebApi+SqlSugar (一)_第2张图片

 开发框架Furion之WebApi+SqlSugar (一)_第3张图片

开发框架Furion之WebApi+SqlSugar (一)_第4张图片

2.2 创建Start类库

创建名称为 MyFurion.Start的类库

解决方案右击——添加——新项目——类库

开发框架Furion之WebApi+SqlSugar (一)_第5张图片

开发框架Furion之WebApi+SqlSugar (一)_第6张图片 开发框架Furion之WebApi+SqlSugar (一)_第7张图片

 开发框架Furion之WebApi+SqlSugar (一)_第8张图片

2.3创建Model实体类库

创建名称为 MyFurion.Model的类库

创建步骤同 2.2创建Startup类库

2.4创建Application仓储业务类库

创建名称为 MyFurion.Application的类库

创建步骤同 2.2创建Startup类库

2.5创建Unility通用方法类库

创建名称为 MyFurion.Unility的类库

创建步骤同 2.2创建Startup类库

至此需要的类库项目创建完成

3.基础功能配置

3.1 Model实体对象与数据表映射

MyFurion.Model项目中,通过Nuget添加Furion、Furion.Extras.DatabaseAccessor.SqlSugar、Furion.Extras.ObjectMapper.Mapster、SqlSugarCore,同时添加对项目MyFurion.Unility的引用

创建实体基类BaseEntity

using SqlSugar;
using System.Text.Json.Serialization;
using MyFurion.Unility.Const;
namespace MyFurion.Model
{
    /// 
    /// 实体基类
    /// 
    public class BaseEntity
    {
        /// 
        /// 
        /// 
        public BaseEntity()
        {
            CreateTime = DateTime.Now;
            IsDeleted = false;
            Id = SnowFlakeSingle.Instance.NextId();
        }
        /// 
        /// id
        /// 
        [SugarColumn(IsPrimaryKey =true,DefaultValue ="主键")]
        public long Id { get; set; }
        /// 
        /// 创建时间
        /// 
        [SugarColumn(IsOnlyIgnoreUpdate = true,ColumnDescription = "创建时间")]
        public DateTime CreateTime { get; set; }
        /// 
        /// 创建人id
        /// 
        [SugarColumn(IsOnlyIgnoreUpdate = true,IsNullable =true, ColumnDescription = "创建人id")]
        [JsonIgnore]
        public string? CreateUserId { get; set; }
        /// 
        /// 创建人
        /// 
        [SugarColumn(IsOnlyIgnoreUpdate = true, IsNullable = true, ColumnDescription = "创建人")]
        [JsonIgnore]
        public string? CreateUser { get; set; }
        /// 
        /// 创建单位id
        /// 
        [SugarColumn(IsOnlyIgnoreUpdate = true, IsNullable = true, ColumnDescription = "创建单位id")]
        [JsonIgnore]
        public string? CreateOrgId { get; set; }
        /// 
        /// 修改时间
        /// 
        [SugarColumn(IsOnlyIgnoreInsert =true,IsNullable =true, ColumnDescription = "修改时间")]
        public DateTime? ModifyTime { get; set; }
        /// 
        /// 修改人id
        /// 
        [SugarColumn(IsOnlyIgnoreInsert = true, IsNullable = true, ColumnDescription = "修改人id")]
        [JsonIgnore]
        public string? ModifyUserId { get; set; }
        /// 
        /// 修改人
        /// 
        [SugarColumn(IsOnlyIgnoreInsert = true, IsNullable = true, ColumnDescription = "修改人")]
        [JsonIgnore]
        public string? ModifyUser { get; set; }
        /// 
        /// 删除标识
        /// 
        [SugarColumn(ColumnDescription ="删除标识")]
        public bool IsDeleted { get; set; }
        /// 
        /// 删除时间
        /// 
        [SugarColumn(IsOnlyIgnoreInsert = true, ColumnDescription = "删除时间",IsNullable =true)]
        [JsonIgnore]
        public DateTime? DeleteTime { get; set; }
        /// 
        /// 删除原因
        /// 
        [SugarColumn(IsOnlyIgnoreInsert = true, ColumnDescription = "删除原因", IsNullable = true)]
        public string? DeleteReason { get; set; }
        /// 
        /// 删除人id
        /// 
        [SugarColumn(IsOnlyIgnoreInsert = true, ColumnDescription = "删除人id", IsNullable = true)]
        [JsonIgnore]
        public string? DeleteUserId { get; set; }
        /// 
        /// 删除人
        /// 
        [SugarColumn(IsOnlyIgnoreInsert = true, ColumnDescription = "删除人", IsNullable = true)]
        [JsonIgnore]
        public string? DeleteUser { get; set; }
        /// 
        /// 排序
        /// 
        [SugarColumn(ColumnDescription ="排序")]
        public int SortNum { get; set; }
        /// 
        /// 备注
        /// 
        [SugarColumn(ColumnDescription = "备注", IsNullable =true,ColumnDataType =CommonConst.DB_STRING_MAX)]
        public string? Remark { get; set; }
        /// 
        /// 多租户ID
        /// 
        [SugarColumn(ColumnDescription = "多租户ID", DefaultValue = "0")]
        [JsonIgnore]
        public long TenantId { get; set; }
    }
}

3.2 基类仓储及动态Api接口配置

MyFurion.Application项目中,通过Nuget添加SqlSugar.IOC,同时添加对MyFurion.Model项目的引用

新增Dtos、Repository、Controller三个文件夹,分别用于查询条件及输出信息的实体对象、业务代码、Api接口文件

在Dtos文件加下创建PageResult类(分页结果)及PageBaseInput(分页查询条件基类)

namespace MyFurion.Application.Dtos
{
    /// 
    /// 分页数据信息
    /// 
    /// 
    public class PageResult
    {
        /// 
        /// 页码
        /// 
        public int PageIndex { get; set; }
        /// 
        /// 分页大小
        /// 
        public int PageSize { get; set; }
        /// 
        /// 页总数
        /// 
        public int TotalPage { get; set; }
        /// 
        /// 记录总数
        /// 
        public int TotalCount { get; set; }
        /// 
        /// 记录集合
        /// 
        public List Items { get; set; } = new();
    }
}
namespace MyFurion.Application.Dtos
{
    /// 
    /// 分页查询条件基类
    /// 
    public class PageBaseInput
    {
        /// 
        /// 页码
        /// 
        public int PageIndex { get; set; } = 1;
        /// 
        /// 分页大小
        /// 
        public int PageSize { get; set; } = 20;
        /// 
        /// 开始日期
        /// 
        public DateTime? StartTime { get; set; }
        /// 
        /// 结束日期
        /// 
        public DateTime? EndTime { get; set; }
    }
}

 创建GlobalUsings.cs全局引用配置类

global using System.Reflection;
global using System.ComponentModel.DataAnnotations;
global using System.Linq.Expressions;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Http;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.CodeAnalysis;
global using Furion;
global using Furion.DataEncryption;
global using Furion.DataValidation;
global using Furion.DependencyInjection;
global using Furion.DynamicApiController;
global using Furion.Extensions;
global using Furion.FriendlyException;
global using Furion.Logging;
global using SqlSugar;
global using Mapster;
global using SqlSugar.IOC;
global using MyFurion.Model;
global using MyFurion.Application.Dtos;

创建仓储基类BaseRepository

namespace MyFurion.Application
{
    /// 
    /// 仓储基类
    /// 
    /// 
    public class BaseRepository : SimpleClient where T : BaseEntity, new()
    {
        public ITenant itenant = null;//多租户事务
        public BaseRepository(ISqlSugarClient context = null) : base(context)
        {
            //通过特性拿到ConfigId
            var configId = typeof(T).GetCustomAttribute()?.configId;
            if (configId != null)
            {
                Context = DbScoped.SugarScope.GetConnectionScope(configId);//根据类传入的ConfigId自动选择
            }
            else
            {
                Context = context ?? DbScoped.SugarScope.GetConnectionScope(0);//没有默认db0
            }
            //Context = DbScoped.SugarScope.GetConnectionScopeWithAttr();
            itenant = DbScoped.SugarScope;//设置租户接口
        }

        #region 基础业务
        /// 
        /// 新增
        /// 
        /// 
        /// 
        public async Task Add(T t)
        {
            try
            {
                int rowsAffect = await Context.Insertable(t).IgnoreColumns(true).ExecuteCommandAsync();
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"新增失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 批量新增
        /// 
        /// 
        /// 
        public async Task Insert(List t)
        {
            try
            {
                int rowsAffect = await Context.Insertable(t).ExecuteCommandAsync();
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"批量新增失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 插入设置列数据
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task Insert(T parm, Expression> iClumns = null, bool ignoreNull = true)
        {
            try
            {
                int rowsAffect = await Context.Insertable(parm).InsertColumns(iClumns).IgnoreColumns(ignoreNullColumn: ignoreNull).ExecuteCommandAsync();
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"插入设置列数据失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 更新
        /// 
        /// 
        /// 
        /// 
        public async Task Update(T entity, bool ignoreNullColumns = false)
        {
            try
            {
                int rowsAffect = await Context.Updateable(entity).IgnoreColumns(ignoreNullColumns).ExecuteCommandAsync();
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"更新失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 根据实体类更新指定列 eg:Update(dept, it => new { it.Status });只更新Status列,条件是包含
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task Update(T entity, Expression> expression, bool ignoreAllNull = false)
        {
            try
            {
                int rowsAffect = await Context.Updateable(entity).UpdateColumns(expression).IgnoreColumns(ignoreAllNull).ExecuteCommandAsync();
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"根据实体类更新指定列失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 根据实体类更新指定列 eg:Update(dept, it => new { it.Status }, f => depts.Contains(f.DeptId));只更新Status列,条件是包含
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task Update(T entity, Expression> expression, Expression> where)
        {
            try
            {
                int rowsAffect = await Context.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommandAsync();
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"根据实体类更新指定列失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 更新指定列 eg:Update(w => w.NoticeId == model.NoticeId, it => new SysNotice(){ UpdateTime = DateTime.Now, Title = "通知标题" });
        /// 
        /// 
        /// 
        /// 
        public async Task Update(Expression> where, Expression> columns)
        {
            try
            {
                int rowsAffect = await Context.Updateable().SetColumns(columns).Where(where).RemoveDataCache().ExecuteCommandAsync();
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"更新指定列失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 事务 eg:var result = UseTran(() =>{SysRoleRepository.UpdateSysRole(sysRole);DeptService.DeleteRoleDeptByRoleId(sysRole.ID);DeptService.InsertRoleDepts(sysRole);});
        /// 
        /// 
        /// 
        public bool UseTran(Action action)
        {
            try
            {
                var result = Context.Ado.UseTran(() => action());
                return result.IsSuccess;
            }
            catch (Exception ex)
            {
                Context.Ado.RollbackTran();
                Log.Error($"事务执行失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 删除
        /// 
        /// 主键id
        /// 是否真删除
        /// 
        public async Task DeleteById(long id, bool IsDelete = false)
        {
            int rowsAffect = 0;
            try
            {
                if (IsDelete)
                {
                    rowsAffect = await Context.Deleteable().In(id).ExecuteCommandAsync();
                }
                else
                {
                    //假删除 实体属性有isdelete或者isdeleted 请升级到5.0.4.9+,(5.0.4.3存在BUG)
                    rowsAffect = await Context.Deleteable().In(id).IsLogic().ExecuteCommandAsync();
                }
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"删除失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 根据查询条件删除
        /// 
        /// 
        /// 
        /// 
        public async Task DeleteByWhere(Expression> where, bool IsDelete = false)
        {
            int rowsAffect = 0;
            try
            {
                if (IsDelete)
                {
                    rowsAffect = await Context.Deleteable().Where(where).ExecuteCommandAsync();
                }
                else
                {
                    //假删除 实体属性有isdelete或者isdeleted 请升级到5.0.4.9+,(5.0.4.3存在BUG)
                    rowsAffect = await Context.Deleteable().Where(where).IsLogic().ExecuteCommandAsync();
                }
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"根据查询条件删除失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 根据id获取数据
        /// 
        /// 主键值
        /// 泛型实体
        public async Task GetEntityById(long id)
        {
            return await Context.Queryable().FirstAsync(p => p.Id == id);
        }
        /// 
        /// 数据是否存在
        /// 
        /// 
        /// 
        public async Task IsExists(Expression> expression)
        {
            return await Context.Queryable().Where(expression).AnyAsync();
        }
        /// 
        /// 获取所有数据
        /// 
        /// 
        public async Task> GetAll()
        {
            return await Context.Queryable().ToListAsync();
        }
        /// 
        /// 根据查询条件获取数据
        /// 
        /// 
        /// 
        public async Task> GetListByWhere(Expression> expression)
        {
            return await Context.Queryable().Where(expression).ToListAsync();
        }
        /// 
        /// 根据查询条件获取数据(动态表格拼接查询条件)
        /// 
        /// 
        /// 
        public async Task> GetListByWhere(List conditions)
        {
            return await Context.Queryable().Where(conditions).ToListAsync();
        }
        /// 
        /// 根据查询条件获取数据
        /// 
        /// 
        /// 排序字段
        /// 排序方式
        /// 
        public async Task> GetList(Expression> expression, Expression> orderFiled, OrderByType orderEnum = OrderByType.Desc)
        {
            return await Context.Queryable().Where(expression).OrderByIF(orderEnum == OrderByType.Asc, orderFiled, OrderByType.Asc).OrderByIF(orderEnum == OrderByType.Desc, orderFiled, OrderByType.Desc).ToListAsync();
        }
        /// 
        /// 获取分页数据
        /// 
        /// 
        /// 
        /// 
        /// 
        public PageResult GetPageList(Expression> expression, int pageIndex, int pageSize)
        {
            int totalCount = 0;
            var result = Context.Queryable().Where(expression).ToPageList(pageIndex, pageSize, ref totalCount);
            var pageResult = new PageResult();
            pageResult.Items = result;
            pageResult.TotalCount = totalCount;
            pageResult.TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize);
            return pageResult;
        }
        /// 
        /// 获取分页数据
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task> GetPageListAsync(Expression> expression, int pageIndex, int pageSize)
        {
            RefAsync totalCount = 0;
            var result = await Context.Queryable().Where(expression).ToPageListAsync(pageIndex, pageSize, totalCount);
            var pageResult = new PageResult();
            pageResult.Items = result;
            pageResult.TotalCount = totalCount;
            pageResult.TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize);
            return pageResult;
        }
        /// 
        /// 获取分页数据
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public PageResult GetPageList(Expression> expression, int pageIndex, int pageSize, Expression> orderFiled, OrderByType orderEnum = OrderByType.Desc)
        {
            int totalCount = 0;
            var result = Context.Queryable().Where(expression).OrderByIF(orderEnum == OrderByType.Asc, orderFiled, OrderByType.Asc).OrderByIF(orderEnum == OrderByType.Desc, orderFiled, OrderByType.Desc)
                .ToPageList(pageIndex, pageSize, ref totalCount);
            var pageResult = new PageResult();
            pageResult.Items = result;
            pageResult.TotalCount = totalCount;
            pageResult.TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize);
            return pageResult;
        }
        /// 
        /// 获取分页数据
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task> GetPageListAsync(Expression> expression, int pageIndex, int pageSize, Expression> orderFiled, OrderByType orderEnum = OrderByType.Desc)
        {
            RefAsync totalCount = 0;
            var result = await Context.Queryable().Where(expression).OrderByIF(orderEnum == OrderByType.Asc, orderFiled, OrderByType.Asc).OrderByIF(orderEnum == OrderByType.Desc, orderFiled, OrderByType.Desc)
                .ToPageListAsync(pageIndex, pageSize, totalCount);
            var pageResult = new PageResult();
            pageResult.Items = result;
            pageResult.TotalCount = totalCount;
            pageResult.TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize);
            return pageResult;
        }
        /// 
        /// 获取分页数据
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task> GetOffsetPageListAsync(Expression> expression, int pageIndex, int pageSize, Expression> orderFiled, OrderByType orderEnum = OrderByType.Desc)
        {
            RefAsync totalCount = 0;
            var result = await Context.Queryable().Where(expression).OrderByIF(orderEnum == OrderByType.Asc, orderFiled, OrderByType.Asc).OrderByIF(orderEnum == OrderByType.Desc, orderFiled, OrderByType.Desc)
                .ToOffsetPageAsync(pageIndex, pageSize, totalCount);
            var pageResult = new PageResult();
            pageResult.Items = result;
            pageResult.TotalCount = totalCount;
            pageResult.TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize);
            return pageResult;
        }
        #endregion

        #region 海量业务高性能
        /// 
        /// 新增(对于海量数据并且性能要高的)
        /// 
        /// 
        /// 
        public async Task BulkAdd(T t)
        {
            try
            {
                int rowsAffect = await Context.Storageable(t).ToStorage().BulkCopyAsync();
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"新增失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 批量新增(对于海量数据并且性能要高的)
        /// 
        /// 
        /// 
        public async Task BatchBulkAdd(List t)
        {
            try
            {
                int rowsAffect = await Context.Storageable(t).ToStorage().BulkCopyAsync();
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"批量新增失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 更新(对于海量数据并且性能要高的)
        /// 
        /// 
        /// 
        public async Task BulkUpdate(T entity)
        {
            try
            {
                int rowsAffect = await Context.Storageable(entity).ToStorage().BulkUpdateAsync();
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"更新失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 批量更新(对于海量数据并且性能要高的)
        /// 
        /// 
        /// 
        public async Task BatchBulkUpdate(List t)
        {
            try
            {
                Context.QueryFilter = new QueryFilterProvider();//清空过滤器 否则会出现Parameter '@IsDelete0' must be defined错误
                int rowsAffect = await Context.Storageable(t).ToStorage().BulkUpdateAsync();
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"更新失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 批量更新(对于海量数据并且性能要高的)
        /// 
        /// 
        /// 
        /// 
        public async Task BatchBulkUpdate(List t, string[] updateColumns)
        {
            try
            {
                Context.QueryFilter = new QueryFilterProvider();//清空过滤器 否则会出现Parameter '@IsDelete0' must be defined错误
                int rowsAffect = await Context.Storageable(t).ToStorage().BulkUpdateAsync(updateColumns);
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"更新失败:{ex.Message}");
                return false;
            }
        }
        #endregion

        #region 存储过程
        /// 
        /// 存储过程
        /// 
        /// 
        /// 
        /// 
        public async Task ProcedureQuery(string procedureName, object parameters)
        {
            return await Context.Ado.UseStoredProcedure().GetDataTableAsync(procedureName, parameters);
        }
        /// 
        /// 存储过程
        /// 
        /// 
        /// 
        /// 
        public async Task> ProcedureQueryList(string procedureName, object parameters)
        {
            return await Context.Ado.UseStoredProcedure().SqlQueryAsync(procedureName, parameters);
        }
        #endregion

        #region Fastest
        /// 
        /// 批量新增
        /// 
        /// 
        /// 
        public async Task BatchFastestkAdd(List t)
        {
            try
            {
                int rowsAffect = await Context.Fastest().BulkCopyAsync(t);
                return rowsAffect > 0;
            }
            catch (Exception ex)
            {
                Log.Error($"fastest批量新增失败:{ex.Message}");
                return false;
            }
        }
        /// 
        /// 批量更新
        /// 
        /// 
        /// 
        public async Task BatchFastestUpdate(List t)
        {
            try
            {
                Context.QueryFilter = new QueryFilterProvider();//清空过滤器 否则会出现Parameter '@IsDelete0' must be defined错误
                int rowsAffect = await Context.Fastest().BulkUpdateAsync(t);
                return rowsAffect >= 0;
            }
            catch (Exception ex)
            {
                Log.Error($"fastest批量更新失败:{ex.Message}");
                return false;
            }
        }
        #endregion
    }
}

 创建applicationsettings.json配置文件,用于配置api接口的风格及分组显示等

{
  "$schema": "https://gitee.com/dotnetchina/Furion/raw/net6/schemas/v3/furion-schema.json",
  /*swagger文档描述配置*/
  "SpecificationDocumentSettings": {
    "DocumentTitle": "MyFurion | 规范化接口",
    "DocExpansionState": "None", //文档展开方式
    "GroupOpenApiInfos": [
      {
        "Group": "Default",
        "Title": "MyFurion API接口",
        "Description": "我的Furion",
        "Version": "1.0.0",
        "TermsOfService": "",
        "Contact": {
          "Name": "Furion",
          "Url": "",
          "Email": ""
        },
        "License": {
          "Name": "Apache-2.0",
          "Url": ""
        }
      }
    ]
  },
  /* controller 接口风格设置*/
  "DynamicApiControllerSettings": {
    "KeepName": true,
    "KeepVerb": true,
    "LowercaseRoute": false,
    "AsLowerCamelCase": true,
    "UrlParameterization": true,
    "VerbToHttpMethods": [
      //[ "getall", "HEAD" ], // => getall 会被复写为 `[HttpHead]`
      //[ "other", "PUT" ] // => 新增一条新规则,比如,一 `[other]` 开头会转换为 `[HttpPut]` 请求
    ]
  },
  /*
  跨域配置
  PolicyName:跨域策略名,string 类型,必填,默认 App.Cors.Policy
  WithOrigins:允许跨域的域名列表,string[] 类型,默认 *
  WithHeaders:请求表头,没有配置则允许所有表头,string[] 类型
  WithExposedHeaders:设置客户端可获取的响应标头,string[] 类型,默认 ["access-token", "x-access-token"]
  WithMethods:设置跨域允许请求谓词,没有配置则允许所有,string[] 类型
  AllowCredentials:是否允许跨域请求中的凭据,bool 类型,默认值 true
  SetPreflightMaxAge:设置预检过期时间,int 类型,默认值 24小时
  FixedClientToken:是否默认配置 WithExposedHeaders,bool 类型,默认 true
  SignalRSupport:是否启用 SignalR 跨域支持,bool 类型,默认 false
  */
  "CorsAccessorSettings": {
    "SignalRSupport": true, //是否启用 SignalR 跨域支持,bool 类型,默认 false
    //设置客户端可获取的响应标头,string[] 类型,默认 ["access-token", "x-access-token"]
    "WithExposedHeaders": [ "access-token", "x-access-token", "environment", "Content-Disposition" ]
  }
}

开发框架Furion之WebApi+SqlSugar (一)_第9张图片

3.3 数据库IOC注册

在MyFurion.Start项目中,通过Nuget添加 AspNetCoreRateLimit、System.Linq.Dynamic.Core,同时添加对项目MyFurion.Application的引用

创建GlobalUsings类配置全局引用

global using System.Reflection;
global using System.Linq.Expressions;
global using Microsoft.Extensions.DependencyInjection;
global using Furion;
global using Furion.Logging;
global using SqlSugar;
global using SqlSugar.IOC;
global using System.Linq.Dynamic.Core;
global using MyFurion.Model;
global using MyFurion.Unility.Const;

创建SqlSugarSetup类,实现sqlsugar数据库IOC注册、CodeFirst、全局过滤器等功能的实现

namespace MyFurion.Start
{
    /// 
    /// sqlsugarIOC注册
    /// 
    public static class SqlSugarSetup
    {
        public static void AddSqlsugarSetup(IServiceCollection services)
        {
            List iocConfigs = App.GetConfig>("ConnectionConfigs");//获取数据库连接配置
            SugarIocServices.AddSqlSugar(iocConfigs);
            SugarIocServices.ConfigurationSugar(db =>
            {
                foreach (var iocItem in iocConfigs)
                {
                    SqlSugarProvider dbClient = db.GetConnection(iocItem.ConfigId);
                    SetQueryFilter(dbClient);
                    dbClient.Aop.OnLogExecuting = (sql, pars) =>
                    {
                        Log.Information(SqlProfiler.ParameterFormat(sql, pars));
                        Console.WriteLine(SqlProfiler.ParameterFormat(sql, pars));
                        Console.WriteLine();
                    };
                    var dbtype = dbClient.CurrentConnectionConfig.DbType;
                    dbClient.CurrentConnectionConfig.ConfigureExternalServices = new ConfigureExternalServices()
                    {
                        //自定义类型多库兼容
                        EntityService = (c, p) =>
                        {
                            if (p.DataType == CommonConst.DB_STRING_MAX)
                            {
                                if (dbtype == DbType.MySql)
                                {
                                    p.DataType = "longtext";
                                }
                                else if (dbtype == DbType.SqlServer)
                                {
                                    p.DataType = "nvarchar(max)";
                                }
                            }

                        }
                    };
                }
            });
            CreateTable(iocConfigs);
        }
        /// 
        /// 创建数据库表 codefirst
        /// 
        private static void CreateTable(List iocConfigs)
        {
            foreach (var item in iocConfigs)
            {
                string configId = item.ConfigId;
                ISqlSugarClient db = DbScoped.SugarScope.GetConnectionScope(configId);
                db.DbMaintenance.CreateDatabase();//没有数据库的时候创建数据库
                var tableLists = db.DbMaintenance.GetTableInfoList();
                var files = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "MyFurion.Model.dll");
                if (files.Length > 0)
                {
                    Type[] types = Assembly.LoadFrom(files[0]).GetTypes().Where(it => it.BaseType == typeof(BaseEntity)).ToArray();
                    //Type[] types = Assembly.LoadFrom(files[0]).GetTypes().ToArray();
                    foreach (var entityType in types)
                    {
                        //创建数据表
                        string tableName = entityType.GetCustomAttribute().TableName.ToLower();//根据特性获取表名称
                        var configid = entityType.GetCustomAttribute()?.configId;//根据特性获取租户id
                        configid = configid == null ? "0" : configid.ToString();
                        if (!tableLists.Any(p => p.Name == tableName) && configId == configid.ToString())
                        {
                            //创建数据表包括字段更新
                            db.CodeFirst.InitTables(entityType);
                        }
                    }
                    db.Close();
                }
            }
        }
        /// 
        /// 添加全局过滤器
        /// 
        /// 
        private static void SetQueryFilter(SqlSugarProvider provider)
        {
            //添加全局过滤器
            var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "MyFurion.Model.dll");
            if (files.Length > 0)
            {
                Type[] types = Assembly.LoadFrom(files[0]).GetTypes().Where(it => it.BaseType == typeof(BaseEntity)).ToArray();
                foreach (var entityType in types)
                {
                    //string tableName = entityType.GetCustomAttribute().TableName;//根据特性获取表名称
                    var lambda = DynamicExpressionParser.ParseLambda( new[] { Expression.Parameter(entityType, "it") },typeof(bool), $"{nameof(BaseEntity.IsDeleted)} ==  @0",false);
                    provider.QueryFilter.Add(new TableFilterItem(entityType, lambda, true)); //将Lambda传入过滤器
                }
            }
            //插入/更新过滤器,用于审计日志
            provider.Aop.DataExecuting = (oldValue, entityInfo) =>
            {
                if (entityInfo.OperationType == DataFilterType.InsertByObject)
                {
                    //if (entityInfo.PropertyName == "CreatedUId")
                    //{
                    //    entityInfo.SetValue(CurrentUserInfo.UId.ToString());//CreatedUId
                    //}
                    //if (entityInfo.PropertyName == "CreatedUName")
                    //{
                    //    entityInfo.SetValue(CurrentUserInfo.Name);
                    //}
                    //if (entityInfo.PropertyName == "CreateOrgId")
                    //{
                    //    entityInfo.SetValue(CurrentUserInfo.OrgId.ToString());
                    //}
                    //if (entityInfo.PropertyName == "CreateOrgName")
                    //{
                    //    entityInfo.SetValue(CurrentUserInfo.OrgName.ToString());
                    //}
                }
                //update生效        
                if (entityInfo.OperationType == DataFilterType.UpdateByObject)
                {
                    //if (entityInfo.PropertyName == "UpdatedTime")
                    //{
                    //    entityInfo.SetValue(DateTimeOffset.Now);//修改UpdateTime字段
                    //}
                    //if (entityInfo.PropertyName == "UpdatedUId")
                    //{
                    //    entityInfo.SetValue(CurrentUserInfo.UId.ToString());//修改UpdateTime字段
                    //}
                    //if (entityInfo.PropertyName == "UpdatedUName")
                    //{
                    //    entityInfo.SetValue(CurrentUserInfo.Name);//修改UpdateTime字段
                    //}
                }
            };
        }
    }
} 
  

 3.4 Startup配置

在MyFurion.Start项目中创建Handlers文件夹,然后创建XnRestfulResultProvider类,自定义接口规范化输出数据格式

using Furion.DataValidation;
using Furion.FriendlyException;
using Furion.UnifyResult;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace MyFurion.Start
{
    /// 
    /// 规范化RESTful风格返回值
    /// 
    [UnifyModel(typeof(XnRestfulResult<>))]
    public class XnRestfulResultProvider : IUnifyResultProvider
    {
        /// 
        /// 异常返回值
        /// 
        /// 
        /// 
        /// 
        public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
        {
            return new JsonResult(new XnRestfulResult
            {
                Code = metadata.StatusCode,
                Success = false,
                Data = null,
                Message = context.Exception.Message,// metadata.Errors,
                //Extras = UnifyContext.Take(),
                //Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
            });
        }

        /// 
        /// 成功返回值
        /// 
        /// 
        /// 
        /// 
        public IActionResult OnSucceeded(ActionExecutedContext context, object data)
        {
            return new JsonResult(new XnRestfulResult
            {
                Code = StatusCodes.Status200OK,// context.Result is EmptyResult ? StatusCodes.Status204NoContent : StatusCodes.Status200OK,  // 处理没有返回值情况 204
                Success = true,
                Data = data,
                Message = "请求成功",
                //Extras = UnifyContext.Take(),
                //Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
            });
        }

        /// 
        /// 验证失败返回值
        /// 
        /// 
        /// 
        /// 
        public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
        {
            return new JsonResult(new XnRestfulResult
            {
                Code = StatusCodes.Status400BadRequest,
                Success = false,
                Data = null,
                Message = metadata.Message,
                //Extras = UnifyContext.Take(),
                //Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
            });
        }

        /// 
        /// 处理输出状态码
        /// 
        /// 
        /// 
        /// 
        /// 
        public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings)
        {
            // 设置响应状态码
            UnifyContext.SetResponseStatusCodes(context, statusCode, unifyResultSettings);

            switch (statusCode)
            {
                // 处理 401 状态码
                case StatusCodes.Status401Unauthorized:
                    await context.Response.WriteAsJsonAsync(new XnRestfulResult
                    {
                        Code = StatusCodes.Status401Unauthorized,
                        Success = false,
                        Data = null,
                        Message = "401 登录已过期,请重新登录",
                        //Extras = UnifyContext.Take(),
                        //Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                    }, App.GetOptions()?.JsonSerializerOptions);
                    break;
                // 处理 403 状态码
                case StatusCodes.Status403Forbidden:
                    await context.Response.WriteAsJsonAsync(new XnRestfulResult
                    {
                        Code = StatusCodes.Status403Forbidden,
                        Success = false,
                        Data = null,
                        Message = "403 禁止访问,没有权限",
                        //Extras = UnifyContext.Take(),
                        //Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                    }, App.GetOptions()?.JsonSerializerOptions);
                    break;
                default:
                    break;
            }
        }
    }

    /// 
    /// RESTful风格---XIAONUO返回格式
    /// 
    /// 
    public class XnRestfulResult
    {
        /// 
        /// 执行成功
        /// 
        public bool Success { get; set; }
        /// 
        /// 状态码
        /// 
        public int? Code { get; set; }
        /// 
        /// 错误信息
        /// 
        public virtual string Message { get; set; } = String.Empty;
        /// 
        /// 数据
        /// 
        public T? Data { get; set; }
        / 
        / 附加数据
        / 
        //public  object Extras { get; set; }
        / 
        / 时间戳
        / 
        //public long Timestamp { get; set; }
    }
}
 
  

在MyFurion.Start项目中创建Startup类,用于Service注册、日志、JSON序列化、Swagger等配置

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace MyFurion.Start
{
    /// 
    /// 
    /// 
    public class Startup:AppStartup
    {
        /// 
        /// 
        /// 
        /// 
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSensitiveDetection();//注册脱敏词汇检测服务
            services.AddControllers().AddNewtonsoftJson();//防止json数据类型转换失败
            services.AddControllers().AddInjectWithUnifyResult();//规范化输出设置
            services.AddCorsAccessor();//配置跨域                 
            //统一日期类型返回
            services.AddControllersWithViews().AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            });
            services.Configure(options =>
            {
                options.Limits.MaxRequestBodySize = int.MaxValue;
            });
            //设置日志
            Array.ForEach(new[] { LogLevel.Information, LogLevel.Error }, logLevel =>
            {
                services.AddFileLogging("Logs/{1}-{0:yyyy}-{0:MM}-{0:dd}-{0:HH}.log", options =>
                {
                    options.FileNameRule = fileName => string.Format(fileName, DateTime.UtcNow, logLevel.ToString());
                    options.WriteFilter = logMsg => logMsg.LogLevel == logLevel;
                    options.Append = true;
                    //options.MessageFormat = (logMsg) =>
                    //{
                    //    var stringBuilder = new System.Text.StringBuilder();
                    //    stringBuilder.Append(System.DateTime.Now.ToString("o"));
                    //    // 其他的。。。自己组装
                    //    return stringBuilder.ToString();
                    //};
                });
            });
            SqlSugarSetup.AddSqlsugarSetup(services);        
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //  NGINX 反向代理获取真实IP
            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });
            app.UseUnifyResultStatusCodes();// 添加状态码拦截中间件 添加规范化结果状态码        
            app.UseHttpsRedirection();// 强制https
            app.UseStaticFiles(); //启用静态文件
            app.UseRouting();
            app.UseCorsAccessor();//跨域中间件
            //开启身份认证
            //app.UseAuthentication();
            //app.UseAuthorization();
            app.UseInject("MyFurion");
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

3.5 Swagger配置

 在项目MyFurion.Model、MyFurion.Application、MyFurion.Start三个项目Debug及Release模式下设置api XML文件输出

以MyFurion.Model为配置示例

开发框架Furion之WebApi+SqlSugar (一)_第10张图片

然后在Startup中的Configure中添加注册Inject 

app.UseInject("MyFurion");//MyFurion swagger文档的路由前缀

配置项目默认启动页为Swagger

MyFurion.WebApi项目中,Properties/launchSettings.json配置文件中,将launchUrl修改为配置的Swagger路由地址 

开发框架Furion之WebApi+SqlSugar (一)_第11张图片

开发框架Furion之WebApi+SqlSugar (一)_第12张图片

3.6启动配置

MyFurion.WebApi项目 删除Controllers文件夹及WeatherForecast文件,卸载Nuget中对Swagger的引用,添加对项目MyFurion.Start的引用

Program.cs中的代码改为

Serve.Run(RunOptions.Default);

appSettings.json配置文件内容改为

{
  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.EntityFrameworkCore": "Information"
    }
  },
  "AllowedHosts": "*",
  /*数据库连接配置
   ConnectionString:连接字符串
   DbType:数据库类型 支持MySql = 0,SqlServer = 1,Sqlite = 2,Oracle = 3,PostgreSQL = 4,Dm = 5,Kdbndp = 6,Oscar = 7,MySqlConnector = 8,Access = 9,OpenGauss = 10,Custom = 900
   ConfigId:租户id
   IsAutoCloseConnection:自动释放和关闭数据库连接,如果有事务事务结束时关闭,否则每次操作后关闭
   AllowLoadLocalInfile:大数据写入是 mysql数据配置必须
  */
  "ConnectionConfigs": [
    {
      "ConnectionString": "Data Source=.;User ID=sa;Password=123456;Initial Catalog=MyFurionTest",
      "DbType": 1,
      "ConfigId": "0",
      "IsAutoCloseConnection": true
    }
  ],
  "AppSettings": {
    "InjectSpecificationDocument": true //如果不需要线上环境开启 Swagger 功能,则设置为false 修改时需要重新发布
  }
}

4.项目使用示例展示

在Furion.Model中创建Org实体对象,用于验证CodeFirst功能

namespace MyFurion.Model
{
    /// 
    /// 组织机构信息
    /// 
    [SugarTable("Sys_Org")]
    [Tenant(0)]
    public class OrgInfo:BaseEntity
    {
        /// 
        /// 机构编码
        /// 
        [SugarColumn(IsNullable =true,ColumnDescription ="机构编码")]
        public string? OrgCode { get; set; }
        /// 
        /// 机构名称
        /// 
        [SugarColumn(IsNullable = true, ColumnDescription = "机构名称")]
        public string? OrgName { get; set; }
    }
}

在MyFurion.Application项目中创建OrgRepository类,用于实现业务代码的仓储类

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

namespace MyFurion.Application
{
    /// 
    /// 机构服务仓储
    /// 
    public class OrgRepository:BaseRepository,ITransient
    {
        //TODO
    }
}

在MyFurion.Application项目中,创建Controller文件夹,存放接口文件

创建FurionTestController

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

namespace MyFurion.Application.Controller
{
    /// 
    /// furionTest
    /// 
    [ApiDescriptionSettings(Name = "FurionTest", Order = 1)]
    [Route("api/furionTest")]
    public class FurionTestController:IDynamicApiController
    {
        private readonly OrgRepository _orgRepository;
        public FurionTestController(OrgRepository orgRepository)
        {
            _orgRepository = orgRepository;
        }
        /// 
        /// furionTestGet
        /// 
        /// 
        [HttpGet("furionHello")]
        public string GetHello()
        {
            return "Hello Furion";
        }
        /// 
        /// post test
        /// 
        /// 
        /// 
        [HttpPost("testPost")]
        public string TestPost(TestPostData data)
        {
            return "Hello Post";
        }
        /// 
        /// 获取组织机构信息
        /// 
        /// 
        [HttpGet("getOrgList")]
        public async Task> GetOrgList()
        {
            return await _orgRepository.GetAll();
        }
    }

    public class TestPostData
    {
        public string? DataValue { get; set; }

        public int TestTimes { get; set; }
    }
}

数据库生成结果

开发框架Furion之WebApi+SqlSugar (一)_第13张图片

 项目启动页

开发框架Furion之WebApi+SqlSugar (一)_第14张图片

 最终项目架构

开发框架Furion之WebApi+SqlSugar (一)_第15张图片

你可能感兴趣的:(.Net,.net,Furion)