feat(新增): EFCore做逻辑删除

This commit is contained in:
Bunny 2024-08-30 20:27:48 +08:00
parent 04f0c79c2e
commit 4c8672a42b
13 changed files with 148 additions and 41 deletions

View File

@ -1,15 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="bunny" uuid="1a151f4f-7b33-40a5-b425-02b84edb60e6">
<data-source source="LOCAL" name="bunny" uuid="b6cf8a1e-027c-4a20-8eff-d18024c58281">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:D:\MyFolder\Bunny\Bunny-cli\CSharp\CSharp-Single-EFCore\Bunny.WebApi\bunny.db</jdbc-url>
<jdbc-url>jdbc:sqlite:D:\Project\web\Bunny-Cli\template\CSharp\CSharp-Single-EFCore\Bunny.WebApi\Database\bunny.db</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>

View File

@ -28,14 +28,9 @@ public class EfCoreContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 添加过滤器
modelBuilder.Entity<BaseEntity>()
.HasQueryFilter(e => !e.IsDeleted);
// 默认值为false表示未删除
modelBuilder.Entity<BaseEntity>()
.Property(e => e.IsDeleted)
.HasDefaultValue(false);
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType))
entityType.AddSoftDeleteQueryFilter();
base.OnModelCreating(modelBuilder);
}
@ -74,7 +69,7 @@ public class EfCoreContext : DbContext
if (item.Entity is BaseEntity entity)
switch (item.State)
{
//添加操作
// 添加操作
case EntityState.Added:
{
if (entity.Id == string.Empty) entity.Id = Guid.NewGuid().ToString();
@ -84,10 +79,10 @@ public class EfCoreContext : DbContext
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
break;
}
//修改操作
// 修改操作
case EntityState.Modified:
entity.UpdateTime = DateTime.Now;
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
if (BaseContext.GetUserId() != null) entity.UpdateUserId = BaseContext.GetUserId()!.Value;
break;
case EntityState.Detached:
break;
@ -97,8 +92,6 @@ public class EfCoreContext : DbContext
entity.UpdateTime = DateTime.Now;
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}

View File

@ -5,7 +5,7 @@ namespace Bunny.Common.Connect;
public static class RedisContext
{
public static IDatabase RedisDatabase;
public static IDatabase? RedisDatabase;
private static readonly EndPointCollection EndPointCollection = new();
public static void AddRedisContext(this WebApplicationBuilder builder)

View File

@ -0,0 +1,36 @@
using System.Linq.Expressions;
using System.Reflection;
using Bunny.Dao.Entity.Base;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Bunny.Common.Connect;
/// <summary>
/// 软删除过滤器
/// </summary>
public static class SoftDeleteQueryExtension
{
/// <summary>
/// 软删除过滤器
/// </summary>
/// <param name="entityData"></param>
public static void AddSoftDeleteQueryFilter(this IMutableEntityType entityData)
{
var methodToCall = typeof(SoftDeleteQueryExtension)
.GetMethod(nameof(GetSoftDeleteFilter), BindingFlags.NonPublic | BindingFlags.Static)
?.MakeGenericMethod(entityData.ClrType);
var filter = methodToCall?.Invoke(null, []);
entityData.SetQueryFilter((LambdaExpression)filter!);
}
/// <summary>
/// 软删除
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <returns></returns>
private static LambdaExpression GetSoftDeleteFilter<TEntity>() where TEntity : BaseEntity
{
Expression<Func<TEntity, bool>> filter = x => !x.IsDeleted;
return filter;
}
}

View File

@ -3,33 +3,33 @@
public class BaseContext
{
// 使用Lazy初始化确保线程安全
public static readonly ThreadLocal<long?> UserId = new();
public static readonly ThreadLocal<string?> Token = new();
public static readonly ThreadLocal<long?>? UserId = null;
public static readonly ThreadLocal<string?>? Token = null;
// 用户id相关
public static long? GetUserId()
{
return UserId.Value;
return UserId?.Value;
}
public static void SetUserId(long? userId)
{
UserId.Value = userId;
UserId!.Value = userId;
}
public static string? GetToken()
{
return Token.Value;
return Token?.Value;
}
public static void SetToken(string token)
{
Token.Value = token;
Token!.Value = token;
}
public static void RemoveUser()
{
Token.Value = null;
UserId.Value = null;
if (Token != null) Token.Value = null;
if (UserId != null) UserId.Value = null;
}
}

View File

@ -12,7 +12,5 @@ public class BaseEntity
public long UpdateUserId { get; set; }
public string? OperationMessage { get; set; }
public bool IsDeleted { get; set; }
}

View File

@ -5,5 +5,5 @@ namespace Bunny.Dao.Entity.System;
public class Blog : BaseEntity
{
[Required(ErrorMessage = "邮箱是必填项")] public string Url { get; set; }
[Required(ErrorMessage = "Url是必填项")] public string Url { get; set; }
}

View File

@ -13,7 +13,6 @@ public class JobService : IJobService
public void StartSimpleJob()
{
var schedulerFactory = new StdSchedulerFactory();
// TODO 如果使用异步必须使用await 和 async
// var scheduler = schedulerFactory.GetScheduler();
var scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
@ -32,10 +31,6 @@ public class JobService : IJobService
// 使用同步方法
scheduler.ScheduleJob(jobDetail, trigger).GetAwaiter().GetResult();
scheduler.Start().GetAwaiter().GetResult();
// TODO 使用异步
// await _scheduler.ScheduleJob(jobDetail, trigger);
// await _scheduler.Start();
}
/// <summary>
@ -49,8 +44,8 @@ public class JobService : IJobService
var jobDetail = JobBuilder.Create<SimpleJob>()
.UsingJobData("username", "用户名")
.UsingJobData("password", "密码")
// TODO 如果不设置持久的会报错;
// TODO Jobs added with no trigger must be durable.Quartz.SchedulerException: Jobs added with no trigger must be durable.
// 如果不设置持久的会报错;
// Jobs added with no trigger must be durable.Quartz.SchedulerException: Jobs added with no trigger must be durable.
.StoreDurably() // 设置作业为持久的
.WithIdentity("simpleJob", "简单的JOB")
.Build();
@ -63,9 +58,9 @@ public class JobService : IJobService
.UsingJobData("trigger", "trigger值")
.WithIdentity("testTrigger", "测试发出器")
.StartNow()
// TODO 设置调度时间单位
// 设置调度时间单位
// .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).WithRepeatCount(10))
// TODO 自定义调度时间单位
// 自定义调度时间单位
.WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMinutes(1)).WithRepeatCount(10))
.Build();
@ -95,9 +90,9 @@ public class JobService : IJobService
.UsingJobData("trigger", "trigger值")
.WithIdentity("testTrigger", "测试发出器")
.StartNow()
// TODO StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 0))触发器从每天的20:00晚上8点开始。
// TODO EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0))触发器在每天的22:00晚上10点结束。
// TODO WithIntervalInSeconds(2)在开始和结束时间之间触发器将每2秒触发一次作业。
// StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 0))触发器从每天的20:00晚上8点开始。
// EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0))触发器在每天的22:00晚上10点结束。
// WithIntervalInSeconds(2)在开始和结束时间之间触发器将每2秒触发一次作业。
.WithDailyTimeIntervalSchedule(x => x.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(16, 0))
.EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0))
// 设置工作时间在每周的星期几

View File

@ -11,7 +11,7 @@ public class BaseConfig(WebApplicationBuilder builder)
/// </summary>
public void Initialize()
{
// 配置跨域
// 配置跨域
UseCors();
// 配置日志相关
// builder.Logging.AddLog4Net("Config/log4net.config");
@ -24,8 +24,11 @@ public class BaseConfig(WebApplicationBuilder builder)
// 设置文件最大上传大小
options.Limits.MaxRequestBodySize = 1024 * 1024 * 100;
});
// 添加Service服务
// 注入后台服务
builder.AddApplicationServices();
// 设置控制器返回时间,统一格式
builder.Services.AddControllers().AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
// 添加后台服务
builder.AddApplicationBackendServices();
// 添加验证码

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -62,3 +62,77 @@ service.AddCaptcha(options =>
});
}
```
## AutoFac配置
### 自动注入按照名称注入
**AutoFac配置详情**
```csharp
using Autofac;
using Bunny.Common.Attribute;
using Microsoft.AspNetCore.Mvc;
namespace Bunny.WebApi.Config;
public static class AddAutofacConfig
{
/// <summary>
/// AutoFac自动注入约定名称
/// 接口以Service结尾注入到IOC中
/// </summary>
/// <param name="builder"></param>
public static void BuildContainer(ContainerBuilder builder)
{
// 扫描所以前缀Bunny的命名空间
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(t => t.FullName!.StartsWith("Bunny"));
// 遍历注入服务
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
// 注入Service
var serviceTypes = types.Where(t => t.GetCustomAttributes(typeof(ServiceAttribute), false).Length != 0);
foreach (var serviceType in serviceTypes)
{
var interfaces = serviceType.GetInterfaces();
builder.RegisterType(serviceType).As(interfaces).InstancePerDependency();
}
// 注入数据库相关
var mapperTypes = types.Where(t => t.GetCustomAttributes(typeof(MapperAttribute), false).Length != 0);
foreach (var mapperType in mapperTypes)
{
var interfaces = mapperType.GetInterfaces();
builder.RegisterType(mapperType).As(interfaces).InstancePerDependency();
}
// 注入后台服务
var componentTypes =
types.Where(t => t.GetCustomAttributes(typeof(ComponentAttribute), false).Length != 0);
foreach (var componentType in componentTypes)
builder.RegisterType(componentType).AsSelf().As<IHostedService>().SingleInstance();
}
// 如果不在在 Controller 层写构造函数可以打开这个,自动完成注入
var controllerBaseType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
.PropertiesAutowired(); //支持属性注入
}
}
```
**主程序入口配置**
```csharp
// 让控制器实例由容器创建
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
// 如果将Service放在 WebApi同级目录下可以完成Controller自动注入不需要写构造函数
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
builder.Services.AddControllers().AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
```