添加项目文件。

This commit is contained in:
falcon 2022-03-21 12:05:12 +08:00
parent 618d30ccb9
commit 4aefddc08a
25 changed files with 1179 additions and 0 deletions

View File

@ -0,0 +1,61 @@
using Falcon.SugarApi.BackTask;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading;
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
namespace Falcon.SugarApi.Test
{
[TestClass]
public class BackTaskTest
{
[TestMethod]
public void BackgroundLongTaskTestMethod() {
var task = new BackTaskObject();
Assert.IsTrue(task.State == 0, "初始化状态错误");
var token = new CancellationTokenSource();
task.StartAsync(token.Token);
Thread.Sleep(1 * 1000);
Assert.IsTrue(task.State == 1, "启动状态错误");
Thread.Sleep(5 * 1000);
task.StopAsync(token.Token).Wait();
Thread.Sleep(2 * 1000);
Assert.IsTrue(task.State == 2, "停止状态错误");
Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss")}:测试完成");
}
}
public class BackTaskObject : BackgroundLongTask
{
public override float RunTimespan => 1;
public ILogger Log { get; set; }
public int State { get; set; } = 0;
public BackTaskObject() {
this.Log = new TestLog();
}
protected override void OnStop(BackgroundLongTask t) {
State = 2;
this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test OnStop!");
base.OnStop(t);
}
protected override void OnStart(BackgroundLongTask t) {
State = 1;
this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test OnStart!");
base.OnStart(t);
}
protected override void OnCompleted(BackgroundLongTask t) {
this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test OnCompleted!");
base.OnCompleted(t);
}
protected override void Run() {
this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test Run!");
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5;net6</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Falcon.SugarApi\Falcon.SugarApi.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.Logging;
namespace Falcon.SugarApi.Test
{
/// <summary>
/// 测试用Logger实例
/// </summary>
public class TestLog : ILogger
{
public IDisposable BeginScope<TState>(TState state) {
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) {
Console.WriteLine(state?.ToString());
}
}
}

31
Falcon.SugarApi.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32228.430
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Falcon.SugarApi", "Falcon.SugarApi\Falcon.SugarApi.csproj", "{0E6BB911-30D2-416F-9B82-23B2C7F0C83F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Falcon.SugarApi.Test", "Falcon.SugarApi.Test\Falcon.SugarApi.Test.csproj", "{EADC8D8F-449C-4C4C-8168-0075B333494B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Release|Any CPU.Build.0 = Release|Any CPU
{EADC8D8F-449C-4C4C-8168-0075B333494B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EADC8D8F-449C-4C4C-8168-0075B333494B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EADC8D8F-449C-4C4C-8168-0075B333494B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EADC8D8F-449C-4C4C-8168-0075B333494B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {88773585-6316-44D6-B95A-F419F746F361}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,90 @@
using Falcon.SugarApi.DatabaseDefinitions;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// api接口基础数据操作控制器
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
public abstract class ApiBaseOptionController<T> : ApiControllerBase, IApiBaseControllerOption<T> where T : SugarTableBase, new()
{
/// <summary>
/// 实例化基础数据操作控制器
/// </summary>
/// <param name="service">服务提供器</param>
public ApiBaseOptionController(IServiceProvider service) : base(service) {
}
/// <summary>
/// 插入一条新记录
/// </summary>
/// <param name="data">要增加的数据</param>
/// <returns>增加以后的数据</returns>
[HttpPost]
public virtual T AddOne(T data) {
this.SugarDb.Insert(data, data.CreateBy);
return data;
}
/// <summary>
/// 删除一条数据
/// </summary>
/// <param name="filter">删除的数据条件数据Id</param>
/// <returns>删除后的数据</returns>
[HttpPost]
public virtual T DeleteOne(OneRecoder filter) {
var id = filter.Id;
this.SugarDb.Delete<T>(id, filter.OptionBy);
return this.SugarDb.Queryable<T>().First(m => m.Id == id);
}
/// <summary>
/// 修改数据
/// </summary>
/// <param name="data">要修改的数据</param>
/// <returns>修改后的数据</returns>
[HttpPost]
public virtual T Edit(T data) {
this.SugarDb.Update(data, data.UpdateBy);
return data;
}
/// <summary>
/// 查询一条数据
/// </summary>
/// <param name="filter">数据查询条件提供数据ID</param>
/// <returns>查询到的数据集合</returns>
[HttpPost]
public virtual IEnumerable<T> GetOne(OneRecoder filter) {
return this.SugarDb.Queryable<T>().In(filter.Id).ToList();
}
/// <summary>
/// 获取一条测试数据,仅在测试阶段使用
/// </summary>
/// <returns>测试数据</returns>
[HttpPost]
public virtual T GetTest() {
return new T().SetTestModel() as T;
}
/// <summary>
/// 获取数据列表
/// </summary>
/// <param name="page">分页条件</param>
/// <returns>分页后的数据列表</returns>
/// <exception cref="ApiException">数据异常</exception>
[HttpPost]
public virtual IEnumerable<T> ListAll(PageData page) {
if (page.OrderBy.IsNullOrEmpty()) {
throw new ApiException("必须提供OrderBy分页将按此进行排序。");
}
return this.SugarDb.Queryable<T>().OrderBy(page.OrderBy).ToPageList(page.Page, page.PageSize);
}
/// <summary>
/// 相关表初始化
/// </summary>
/// <param name="types">需要初始化的表类型</param>
protected void DataTableInit(params Type[] types) {
this.SugarDb.UpdateTableStructure(types);
}
}
}

View File

@ -0,0 +1,100 @@
using Falcon.SugarApi.DatabaseDefinitions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// api控制器基类
/// </summary>
[Area("api")]
[ApiController]
[Route("api/[Controller]/[Action]")]
public abstract class ApiControllerBase : ControllerBase
{
/// <summary>
/// 日志记录服务
/// </summary>
public ILogger Logger { get; set; }
/// <summary>
/// 服务集合
/// </summary>
public IServiceProvider Services { get; set; }
/// <summary>
/// Sugar数据库
/// </summary>
public SugarDbContext SugarDb { get; set; }
/// <summary>
/// 获取请求的方法前缀
/// </summary>
protected virtual string Prefix {
get {
var con = this.RouteData.Values["controller"];
var ac = this.RouteData.Values["action"];
return $":{con}:{ac}";
}
}
protected ApiControllerBase(IServiceProvider service) {
this.Services = service;
this.Logger = service.GetService(typeof(ILogger<>).MakeGenericType(GetType())) as ILogger;
this.SugarDb = service.GetService<SugarDbContext>();
}
/// <summary>
/// 记录保存请求和响应日志
/// </summary>
/// <typeparam name="TRequest">请求信息类型</typeparam>
/// <typeparam name="TResponse">响应信息类型</typeparam>
/// <param name="data">请求数据</param>
/// <param name="result">响应数据</param>
protected virtual void SaveLogger<TRequest, TResponse>(TRequest data, TResponse result) {
var requestStr = this.JsonSerialize(data);
var responseStr = this.JsonSerialize(result);
var logmsg = $"{this.Prefix}\n请求消息:{requestStr}\n响应消息{responseStr}";
this.Logger.LogInformation(logmsg);
}
/// <summary>
/// 从对象序列化字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="obj">要序列化的对象</param>
/// <returns>字符串</returns>
protected string JsonSerialize<T>(T obj) {
return JsonSerializer.Serialize<T>(obj, new JsonSerializerOptions {
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
});
}
/// <summary>
/// 从字符串反序列化对象
/// </summary>
/// <typeparam name="T">对象的类型</typeparam>
/// <param name="json">json字符串</param>
/// <returns>对象实例</returns>
protected T JsonDeserialize<T>(string json) {
return JsonSerializer.Deserialize<T>(json);
}
/// <summary>
/// 抛出api异常
/// </summary>
/// <param name="msg">异常消息</param>
/// <param name="innException">内部异常</param>
/// <exception cref="ApiException">api异常</exception>
protected virtual void ThrowApiException(string msg, Exception innException)
=> throw new ApiException(msg, innException);
/// <summary>
/// 抛出api异常
/// </summary>
/// <param name="msg">异常消息</param>
/// <exception cref="ApiException">api异常</exception>
protected virtual void ThrowApiException(string msg)
=> throw new ApiException(msg);
}
}

View File

@ -0,0 +1,26 @@
using System;
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// api异常基类
/// </summary>
public class ApiException : Exception
{
/// <summary>
/// 通过异常信息创建异常
/// </summary>
/// <param name="message">异常信息</param>
public ApiException(string message) : base(message)
{
}
/// <summary>
/// 通过异常信息和内部异常创建异常
/// </summary>
/// <param name="message">异常信息</param>
/// <param name="innerException">内部异常</param>
public ApiException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,60 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// api接口返回值模型定义
/// </summary>
public class ApiResponseTypeModelProvider : IApplicationModelProvider
{
public int Order => 1;
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
foreach (ControllerModel controller in context.Result.Controllers)
{
foreach (ActionModel action in controller.Actions)
{
if (!isApiAction(action))
{
continue;
}
var art = action.ActionMethod.ReturnType;
Type returnType =
art.IsGenericType && art.IsAssignableFrom(typeof(Task<>)) ? art.GenericTypeArguments[0].GetGenericArguments()[0] :
art;
action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status200OK));
action.Filters.Add(new ProducesResponseTypeAttribute(typeof(ExceptionModel), StatusCodes.Status400BadRequest));
}
}
}
/// <summary>
/// 方法是否为api方法
/// </summary>
/// <param name="am">Action模型</param>
/// <returns>是api方法返回true否则false</returns>
protected static bool isApiAction(ActionModel am)
{
if (am.Controller.Attributes.Any(c => c is ApiControllerAttribute))
{
return true;
}
if (am.Attributes.Any(c => c is ApiControllerAttribute))
{
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,21 @@
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// 异常返回模型
/// </summary>
public class ExceptionModel
{
/// <summary>
/// 异常编号
/// </summary>
public string Id { get; set; }
/// <summary>
/// 异常信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 异常发生时间
/// </summary>
public string CreateDateTime { get; set; }
}
}

View File

@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Text;
namespace Falcon.SugarApi.ApiDefinistions
{
public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
{
/// <summary>
/// 日志记录器
/// </summary>
public ILogger Logger { get; set; }
public HttpResponseExceptionFilter(ILogger<HttpResponseExceptionFilter> logger)
{
this.Logger = logger;
}
public int Order { get; } = int.MaxValue - 10;
public void OnActionExecuting(ActionExecutingContext context) { }
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception == null || context.ExceptionHandled)
{
return;
}
var exception = context.Exception;
var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var model = new ExceptionModel
{
Id = Guid.NewGuid().ToString(),
CreateDateTime = now,
Message = exception.Message,
};
if (exception is ApiException)
{
context.Result = new ObjectResult(model) { StatusCode = 400, };
context.ExceptionHandled = true;
}
var logmsg = new StringBuilder();
logmsg.AppendLine($"异常编号:{model.Id}");
logmsg.AppendLine($"错误信息:{model.Message}");
logmsg.AppendLine($"详细信息:");
logmsg.AppendLine($"{exception}");
this.Logger.LogError(logmsg.ToString());
}
}
}

View File

@ -0,0 +1,48 @@
using Falcon.SugarApi.DatabaseDefinitions;
using System.Collections.Generic;
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// 定义api基础实现方法
/// </summary>
/// <typeparam name="T">api对应的数据类型</typeparam>
public interface IApiBaseControllerOption<T> where T : SugarTableBase
{
/// <summary>
/// 增加数据
/// </summary>
/// <param name="data">数据实体</param>
/// <returns>增加后的数据实体</returns>
T AddOne(T data);
/// <summary>
/// 根据id删除一个数据记录
/// </summary>
/// <param name="filter">删除条件仅提供数据id</param>
/// <returns>删除后的数据实体</returns>
T DeleteOne(OneRecoder filter);
/// <summary>
/// 修改实体数据
/// </summary>
/// <param name="data">数据实体</param>
/// <returns>修改后的数据实体</returns>
T Edit(T data);
/// <summary>
/// 获取一个测试数据
/// </summary>
/// <returns>一个测试记录</returns>
T GetTest();
/// <summary>
/// 分页获取数据
/// </summary>
/// <param name="page">分页数据</param>
/// <returns>数据列表</returns>
IEnumerable<T> ListAll(PageData page);
/// <summary>
/// 根据id获取一条记录
/// </summary>
/// <param name="filter">删除条件仅提供数据id</param>
/// <returns>删除后的数据列表</returns>
IEnumerable<T> GetOne(OneRecoder filter);
}
}

View File

@ -0,0 +1,19 @@
using System;
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// 一条记录
/// </summary>
public class OneRecoder
{
/// <summary>
/// 数据ID
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// 操作员
/// </summary>
public string OptionBy { get; set; }
}
}

View File

@ -0,0 +1,21 @@
namespace Falcon.SugarApi.ApiDefinistions
{
/// <summary>
/// 分页数据
/// </summary>
public class PageData
{
/// <summary>
/// 页号码
/// </summary>
public int Page { get; set; } = 0;
/// <summary>
/// 页大小
/// </summary>
public int PageSize { get; set; } = 50;
/// <summary>
/// 排序依据 列名 desc倒序
/// </summary>
public string OrderBy { get; set; } = "ID DESC";
}
}

View File

@ -0,0 +1,73 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Falcon.SugarApi.BackTask
{
/// <summary>
/// 长时间执行的后台任务
/// </summary>
public abstract class BackgroundLongTask : BackgroundService
{
/// <summary>
/// 要执行的任务
/// </summary>
protected abstract void Run();
/// <summary>
/// 执行时间间隔
/// </summary>
public abstract float RunTimespan { get; }
/// <summary>
/// 后台任务开始
/// </summary>
protected virtual void OnStart(BackgroundLongTask t) { }
/// <summary>
/// 后台任务停止
/// </summary>
protected virtual void OnStop(BackgroundLongTask t) { }
/// <summary>
/// 完成一次执行
/// </summary>
protected virtual void OnCompleted(BackgroundLongTask t) { }
/// <summary>
/// 用于定期执行任务的委托
/// </summary>
private Action action;
private CancellationTokenSource TokenSource { get; set; } = new();
/// <summary>
/// 构造一个后台长期任务
/// </summary>
public BackgroundLongTask() {
action = async () => {
OnStart(this);
while (!TokenSource.Token.IsCancellationRequested) {
await Task.Delay(TimeSpan.FromSeconds(RunTimespan))
.ContinueWith(wt => { Run(); }).ContinueWith(wt => OnCompleted(this));
}
OnStop(this);
};
}
/// <summary>
/// 服务器启动时执行
/// </summary>
/// <param name="stoppingToken">退出信号</param>
/// <returns></returns>
protected override Task ExecuteAsync(CancellationToken stoppingToken) {
_ = Task.Factory.StartNew(action, stoppingToken);
return Task.CompletedTask;
}
/// <summary>
/// 停止任务
/// </summary>
/// <param name="cancellationToken">退出标记</param>
/// <returns>任务</returns>
public override Task StopAsync(CancellationToken cancellationToken) {
this.TokenSource.Cancel();
return base.StopAsync(cancellationToken);
}
}
}

View File

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Falcon.SugarApi.BackTask
{
public static class IServiceCollectionExtend
{
/// <summary>
/// 注册BackgroundLongTask后台任务。
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddBackgroundLongTask<T>(this IServiceCollection services)
where T : BackgroundLongTask {
return services.AddHostedService<T>();
}
}
}

View File

@ -0,0 +1,14 @@
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// 实体类支持新建数据方法
/// </summary>
public interface ICreateNew
{
/// <summary>
/// 创建新数据实体
/// </summary>
/// <param name="createBy">创建人</param>
void CreateNew(string createBy);
}
}

View File

@ -0,0 +1,14 @@
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// 实体软删除方法
/// </summary>
public interface IDelete
{
/// <summary>
/// 删除本条数据。软删除
/// </summary>
/// <param name="deleteBy">删除人</param>
void Delete(string deleteBy);
}
}

View File

@ -0,0 +1,14 @@
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// 实体支持修改方法
/// </summary>
public interface IModify
{
/// <summary>
/// 更新数据提示
/// </summary>
/// <param name="updateBy">修改人</param>
void Modify(string updateBy);
}
}

View File

@ -0,0 +1,48 @@
using SqlSugar;
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Linq;
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// SqlSugar数据库连接配置
/// </summary>
public class SugarConnectionConfig : ConnectionConfig
{
/// <summary>
/// 是否使用log
/// </summary>
public bool Log { get; set; }
public SugarConnectionConfig() {
this.ConfigureExternalServices ??= new ConfigureExternalServices { };
this.ConfigureExternalServices.EntityService += (p, c) => {
var pt = p.PropertyType;
if (pt.GetCustomAttribute<RequiredAttribute>() != null) {
c.IsNullable = false;
return;
}
if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>)) {
c.IsNullable = true;
return;
}
//var sc = pt.GetCustomAttribute<SugarColumn>();
//if (sc != null) {
// c.IsNullable = sc.IsNullable;
//}
var isNullableTypes = new Type[] { typeof(string) };
if (isNullableTypes.Contains(pt)) {
c.IsNullable = true;
}
else {
c.IsNullable = false;
}
//if (pt == typeof(string) && pt.GetCustomAttribute<RequiredAttribute>() == null) {
// c.IsNullable = true;
//}
};
}
}
}

View File

@ -0,0 +1,17 @@
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// 记录状态
/// </summary>
public static class RecordStetus
{
/// <summary>
/// 有效记录
/// </summary>
public static string Effective => "有效";
/// <summary>
/// 无效,已删除记录
/// </summary>
public static string Invalid => "无效";
}
}

View File

@ -0,0 +1,193 @@
using Microsoft.Extensions.Logging;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// Sugar数据库上下文
/// </summary>
public class SugarDbContext : SqlSugarClient
{
/// <summary>
/// 通过链接配置ConnectionConfig构造SqlSugarClient数据上下文
/// </summary>
/// <param name="config"></param>
/// <param name="logger">日志记录器</param>
public SugarDbContext(SugarConnectionConfig config, ILogger<SugarDbContext> logger) : base(config) {
this.Logger = logger;
if (config.Log) {
this.Aop.OnLogExecuting = (string sql, SugarParameter[] paras) => {
StringBuilder sb = new();
sb.AppendLine(sql);
sb.AppendLine(this.Utilities.SerializeObject(paras.ToDictionary(it => it.ParameterName, it => it.Value)));
this.Logger.LogInformation(sb.ToString());
};
}
}
/// <summary>
/// 日志记录
/// </summary>
public ILogger<SugarDbContext> Logger { get; }
#region
/// <summary>
/// 在数据库中插入数据
/// </summary>
/// <typeparam name="T">数据实体类型</typeparam>
/// <param name="data">要插入的数据列表</param>
/// <param name="createBy">创建人</param>
public void Insert<T>(List<T> data, string createBy) where T : class, new() {
foreach (var i in data) {
if (i is ICreateNew cn) {
cn.CreateNew(createBy);
}
}
this.Insertable(data).ExecuteCommand();
}
/// <summary>
/// 在数据库中插入数据
/// </summary>
/// <typeparam name="T">数据模型</typeparam>
/// <param name="data">要插入的数据</param>
/// <param name="createBy">创建人</param>
public void Insert<T>(T data, string createBy) where T : class, new() {
this.Insert(new List<T> { data }, createBy);
}
#endregion
#region
/// <summary>
/// 在数据库中更新数据
/// </summary>
/// <typeparam name="T">数据模型</typeparam>
/// <param name="data">要更新的数据</param>
/// <param name="updateBy">更新人</param>
public List<T> Update<T>(List<T> data, string updateBy) where T : class, new() {
foreach (var i in data) {
if (i is IModify cn) {
cn.Modify(updateBy);
}
}
this.Updateable(data).ExecuteCommand();
return data;
}
/// <summary>
/// 修改数据
/// </summary>
/// <typeparam name="T">数据模型</typeparam>
/// <param name="data">要更新的数据</param>
/// <param name="updateBy">更新人</param>
public T Update<T>(T data, string updateBy) where T : class, new() {
this.Update(new List<T> { data }, updateBy);
return data;
}
#endregion
#region
/// <summary>
/// 根据主键调用IDelete实现删除数据记录,软删除并非真正删除数据。
/// </summary>
/// <typeparam name="T">数据实体类型必须继承自SugarTableBase</typeparam>
/// <param name="id">要删除的数据id</param>
/// <param name="deleteBy">删除人</param>
public void Delete<T>(Guid id, string deleteBy) where T : SugarTableBase, new() {
var data = new T();
data.Delete(deleteBy);
this.Updateable<T>()
.Where(m => m.Id == id)
.SetColumns(m => m.Status == data.Status)
.SetColumns(m => m.UpdateBy == data.UpdateBy)
.SetColumns(m => m.UpdateTime == data.UpdateTime)
.ExecuteCommand();
}
/// <summary>
/// 批量软删除对象列表调用实体的IDelete实现删除实体。
/// </summary>
/// <typeparam name="T">实体的类型</typeparam>
/// <param name="data">实体对象列表</param>
/// <param name="deleteBy">删除人</param>
/// <returns>删除后的实体列表</returns>
public List<T> Delete<T>(List<T> data, string deleteBy) where T : class, new() {
foreach (var item in data) {
if (item is IDelete d) {
d.Delete(deleteBy);
}
}
this.Updateable(data).ExecuteCommand();
return data;
}
/// <summary>
/// 对象软删除调用实体的IDelete实现删除实体。
/// </summary>
/// <typeparam name="T">实体的类型</typeparam>
/// <param name="data">实体对象</param>
/// <param name="deleteBy">删除人</param>
/// <returns>删除后的对象</returns>
public T Delete<T>(T data, string deleteBy) where T : class, new() {
this.Delete(new List<T> { data }, deleteBy);
return data;
}
#endregion
#region
/// <summary>
/// 已经初始化的类型列表
/// </summary>
private static readonly List<string> InitdTable = new();
/// <summary>
/// 升级数据库表架构并备份表和数据
/// </summary>
/// <param name="types">要升级的表模型</param>
public void UpdateTableStructure(params Type[] types) {
for (int i = 0; i < types.Length; i++) {
var type = types[i];
if (!InitdTable.Any(m => m == type.FullName)) {
this.CodeFirst.BackupTable().SetStringDefaultLength(200).InitTables(type);
InitdTable.Add(type.FullName);
}
}
}
/// <summary>
/// 升级数据库表架构并备份表和数据
/// </summary>
/// <typeparam name="TableType">表模型</typeparam>
public void UpdateTableStructure<TableType>() {
this.UpdateTableStructure(typeof(TableType));
}
#endregion
#region
public IAdo GetAdo() {
return this.Ado.UseStoredProcedure();
}
/// <summary>
/// 获取一个Oracle游标类型参数用作返回值
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public SugarParameter OracleRefCursor(string name = "v_data") =>
new(name, null, true) { IsRefCursor = true };
public object GetParameters<T>(T data) {
//return SqlSugarTool.GetParameters(new { pageStart = 1, pageEnd = 5, recordCount = 0 });
throw new NotSupportedException();
}
#endregion
}
}

View File

@ -0,0 +1,119 @@
using SqlSugar;
using System;
using System.Reflection;
namespace Falcon.SugarApi.DatabaseDefinitions
{
/// <summary>
/// 表基类。一般表应该继承此类
/// </summary>
public abstract class SugarTableBase : ICreateNew, IModify, IDelete
{
/// <summary>
/// 主键
/// </summary>
[SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public Guid Id { get; set; } = Guid.NewGuid();
/// <summary>
/// 创建人
/// </summary>
[SugarColumn(IsOnlyIgnoreUpdate = true, IsNullable = false, ColumnDescription = "首次创建人")]
public string CreateBy { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(IsOnlyIgnoreUpdate = true, IsNullable = false, ColumnDescription = "首次创建时间")]
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 更新人
/// </summary>
[SugarColumn(IsNullable = false, ColumnDescription = "最近更新人")]
public string UpdateBy { get; set; }
/// <summary>
/// 更新时间
/// </summary>
[SugarColumn(IsNullable = false, ColumnDescription = "最新更新时间")]
public DateTime UpdateTime { get; set; } = DateTime.Now;
/// <summary>
/// 记录状态
/// </summary>
[SugarColumn(IsNullable = false, ColumnDescription = "记录状态。有效 无效")]
public string Status { get; set; } = "有效";
/// <summary>
/// 创建新数据记录。
/// </summary>
/// <param name="createBy">创建人</param>
public virtual void CreateNew(string createBy) {
this.Id = Guid.NewGuid();
this.CreateBy = createBy;
this.CreateTime = DateTime.Now;
this.UpdateBy = this.CreateBy;
this.UpdateTime = this.CreateTime;
this.Status = RecordStetus.Effective;
}
/// <summary>
/// 修改实体数据
/// </summary>
/// <param name="updateBy">修改人</param>
public virtual void Modify(string updateBy) {
this.UpdateBy = updateBy;
this.UpdateTime = DateTime.Now;
}
/// <summary>
/// 删除本条数据。软删除
/// </summary>
/// <param name="deleteBy">删除人</param>
public virtual void Delete(string deleteBy) {
this.UpdateBy = deleteBy;
this.UpdateTime = DateTime.Now;
this.Status = RecordStetus.Invalid;
}
/// <summary>
/// 将实体设置为测试用数据!生成测试数据将丢失原有数据,只在测试情况下使用
/// </summary>
public virtual SugarTableBase SetTestModel() {
foreach (PropertyInfo pro in this.GetType().GetProperties()) {
if (pro.Name == "Id") {
this.Id = Guid.NewGuid();
continue;
}
if (pro.Name == "CreateBy" || pro.Name == "UpdateBy") {
pro.SetValue(this, "admin");
continue;
}
if (pro.Name == "CreateTime" || pro.Name == "UpdateTime") {
pro.SetValue(this, DateTime.Now);
continue;
}
if (pro.Name == "Status") {
this.Status = "Effective";
continue;
}
if (pro.CanWrite && pro.PropertyType == typeof(string)) {
pro.SetValue(this, pro.Name);
continue;
}
}
return this;
}
/// <summary>
/// 设置记录有效
/// </summary>
public virtual SugarTableBase SetEnable() {
this.Status = RecordStetus.Effective;
return this;
}
/// <summary>
/// 设置记录无效
/// </summary>
public virtual SugarTableBase SetDisable() {
this.Status = RecordStetus.Invalid;
return this;
}
}
}

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5;net6</TargetFrameworks>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<Description>Falcon.SugarApi基础定义</Description>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="SqlSugarCore" Version="5.0.6.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net5' ">
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net6' ">
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
using Falcon.SugarApi.ApiDefinistions;
using Falcon.SugarApi.DatabaseDefinitions;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
namespace Falcon.SugarApi
{
public static class IServiceCollectionExtend
{
/// <summary>
/// 注册Falcon.Sugar相关api方法和数据库上下文。
/// </summary>
/// <param name="services">服务集合</param>
/// <param name="config">数据上下文配置节点</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddSugarApiWithDbContext(this IServiceCollection services, SugarConnectionConfig config) {
return services.AddSugarApiDbContext(config).AddApiReturnModelProvider();
}
/// <summary>
/// 注册sugarDbcontext数据上下文
/// </summary>
/// <param name="services">服务集合</param>
/// <param name="builder">用于生成配置实例的方法</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddSugarApiDbContext(this IServiceCollection services, Func<IServiceProvider, SugarConnectionConfig> builder) {
return services.AddSingleton<SugarConnectionConfig>(p => builder(p)).AddTransient<SugarDbContext>();
}
/// <summary>
/// 注册sugarDbcontext数据上下文
/// </summary>
/// <param name="service">服务集合</param>
/// <param name="config">数据库配置</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddSugarApiDbContext(this IServiceCollection service, SugarConnectionConfig config) {
return service.AddSingleton(config).AddTransient<SugarDbContext>();
}
/// <summary>
/// 注册api返回结果模型提供器
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddApiReturnModelProvider(this IServiceCollection services) {
services.TryAddEnumerable(ServiceDescriptor.Transient<IApplicationModelProvider, ApiResponseTypeModelProvider>());
return services;
}
}
}

View File

@ -0,0 +1,21 @@
namespace Falcon.SugarApi
{
/// <summary>
/// 字符串扩展
/// </summary>
internal static class StringExtend
{
/// <summary>
/// 字符串是否为空或null
/// </summary>
/// <param name="str">字符串</param>
/// <returns>空或null为True否则False</returns>
public static bool IsNullOrEmpty(this string str) => str == null || string.IsNullOrEmpty(str);
/// <summary>
/// 字符串是否不为空或null
/// </summary>
/// <param name="str">字符串</param>
/// <returns>与IsNullOrEmpty相反</returns>
public static bool IsNotNullOrEmpty(this string str) => !str.IsNullOrEmpty();
}
}