✨ feat(新增): EFCore做逻辑删除
This commit is contained in:
parent
04f0c79c2e
commit
4c8672a42b
|
@ -1,15 +1,23 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
<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>
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
<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>
|
<jdbc-additional-properties>
|
||||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||||
</jdbc-additional-properties>
|
</jdbc-additional-properties>
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
<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>
|
</data-source>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -28,14 +28,9 @@ public class EfCoreContext : DbContext
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
// 添加过滤器
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
modelBuilder.Entity<BaseEntity>()
|
if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType))
|
||||||
.HasQueryFilter(e => !e.IsDeleted);
|
entityType.AddSoftDeleteQueryFilter();
|
||||||
|
|
||||||
// 默认值为false,表示未删除
|
|
||||||
modelBuilder.Entity<BaseEntity>()
|
|
||||||
.Property(e => e.IsDeleted)
|
|
||||||
.HasDefaultValue(false);
|
|
||||||
|
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +69,7 @@ public class EfCoreContext : DbContext
|
||||||
if (item.Entity is BaseEntity entity)
|
if (item.Entity is BaseEntity entity)
|
||||||
switch (item.State)
|
switch (item.State)
|
||||||
{
|
{
|
||||||
//添加操作
|
// 添加操作
|
||||||
case EntityState.Added:
|
case EntityState.Added:
|
||||||
{
|
{
|
||||||
if (entity.Id == string.Empty) entity.Id = Guid.NewGuid().ToString();
|
if (entity.Id == string.Empty) entity.Id = Guid.NewGuid().ToString();
|
||||||
|
@ -84,10 +79,10 @@ public class EfCoreContext : DbContext
|
||||||
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
|
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//修改操作
|
// 修改操作
|
||||||
case EntityState.Modified:
|
case EntityState.Modified:
|
||||||
entity.UpdateTime = DateTime.Now;
|
entity.UpdateTime = DateTime.Now;
|
||||||
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
|
if (BaseContext.GetUserId() != null) entity.UpdateUserId = BaseContext.GetUserId()!.Value;
|
||||||
break;
|
break;
|
||||||
case EntityState.Detached:
|
case EntityState.Detached:
|
||||||
break;
|
break;
|
||||||
|
@ -97,8 +92,6 @@ public class EfCoreContext : DbContext
|
||||||
entity.UpdateTime = DateTime.Now;
|
entity.UpdateTime = DateTime.Now;
|
||||||
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
|
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ namespace Bunny.Common.Connect;
|
||||||
|
|
||||||
public static class RedisContext
|
public static class RedisContext
|
||||||
{
|
{
|
||||||
public static IDatabase RedisDatabase;
|
public static IDatabase? RedisDatabase;
|
||||||
private static readonly EndPointCollection EndPointCollection = new();
|
private static readonly EndPointCollection EndPointCollection = new();
|
||||||
|
|
||||||
public static void AddRedisContext(this WebApplicationBuilder builder)
|
public static void AddRedisContext(this WebApplicationBuilder builder)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,33 +3,33 @@
|
||||||
public class BaseContext
|
public class BaseContext
|
||||||
{
|
{
|
||||||
// 使用Lazy初始化确保线程安全
|
// 使用Lazy初始化确保线程安全
|
||||||
public static readonly ThreadLocal<long?> UserId = new();
|
public static readonly ThreadLocal<long?>? UserId = null;
|
||||||
public static readonly ThreadLocal<string?> Token = new();
|
public static readonly ThreadLocal<string?>? Token = null;
|
||||||
|
|
||||||
// 用户id相关
|
// 用户id相关
|
||||||
public static long? GetUserId()
|
public static long? GetUserId()
|
||||||
{
|
{
|
||||||
return UserId.Value;
|
return UserId?.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetUserId(long? userId)
|
public static void SetUserId(long? userId)
|
||||||
{
|
{
|
||||||
UserId.Value = userId;
|
UserId!.Value = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? GetToken()
|
public static string? GetToken()
|
||||||
{
|
{
|
||||||
return Token.Value;
|
return Token?.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetToken(string token)
|
public static void SetToken(string token)
|
||||||
{
|
{
|
||||||
Token.Value = token;
|
Token!.Value = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveUser()
|
public static void RemoveUser()
|
||||||
{
|
{
|
||||||
Token.Value = null;
|
if (Token != null) Token.Value = null;
|
||||||
UserId.Value = null;
|
if (UserId != null) UserId.Value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,5 @@ public class BaseEntity
|
||||||
|
|
||||||
public long UpdateUserId { get; set; }
|
public long UpdateUserId { get; set; }
|
||||||
|
|
||||||
public string? OperationMessage { get; set; }
|
|
||||||
|
|
||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
}
|
}
|
|
@ -5,5 +5,5 @@ namespace Bunny.Dao.Entity.System;
|
||||||
|
|
||||||
public class Blog : BaseEntity
|
public class Blog : BaseEntity
|
||||||
{
|
{
|
||||||
[Required(ErrorMessage = "邮箱是必填项")] public string Url { get; set; }
|
[Required(ErrorMessage = "Url是必填项")] public string Url { get; set; }
|
||||||
}
|
}
|
|
@ -13,7 +13,6 @@ public class JobService : IJobService
|
||||||
public void StartSimpleJob()
|
public void StartSimpleJob()
|
||||||
{
|
{
|
||||||
var schedulerFactory = new StdSchedulerFactory();
|
var schedulerFactory = new StdSchedulerFactory();
|
||||||
// TODO 如果使用异步,必须使用await 和 async
|
|
||||||
// var scheduler = schedulerFactory.GetScheduler();
|
// var scheduler = schedulerFactory.GetScheduler();
|
||||||
var scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
|
var scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
@ -32,10 +31,6 @@ public class JobService : IJobService
|
||||||
// 使用同步方法
|
// 使用同步方法
|
||||||
scheduler.ScheduleJob(jobDetail, trigger).GetAwaiter().GetResult();
|
scheduler.ScheduleJob(jobDetail, trigger).GetAwaiter().GetResult();
|
||||||
scheduler.Start().GetAwaiter().GetResult();
|
scheduler.Start().GetAwaiter().GetResult();
|
||||||
|
|
||||||
// TODO 使用异步
|
|
||||||
// await _scheduler.ScheduleJob(jobDetail, trigger);
|
|
||||||
// await _scheduler.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -49,8 +44,8 @@ public class JobService : IJobService
|
||||||
var jobDetail = JobBuilder.Create<SimpleJob>()
|
var jobDetail = JobBuilder.Create<SimpleJob>()
|
||||||
.UsingJobData("username", "用户名")
|
.UsingJobData("username", "用户名")
|
||||||
.UsingJobData("password", "密码")
|
.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() // 设置作业为持久的
|
.StoreDurably() // 设置作业为持久的
|
||||||
.WithIdentity("simpleJob", "简单的JOB")
|
.WithIdentity("simpleJob", "简单的JOB")
|
||||||
.Build();
|
.Build();
|
||||||
|
@ -63,9 +58,9 @@ public class JobService : IJobService
|
||||||
.UsingJobData("trigger", "trigger值")
|
.UsingJobData("trigger", "trigger值")
|
||||||
.WithIdentity("testTrigger", "测试发出器")
|
.WithIdentity("testTrigger", "测试发出器")
|
||||||
.StartNow()
|
.StartNow()
|
||||||
// TODO 设置调度时间单位
|
// 设置调度时间单位
|
||||||
// .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).WithRepeatCount(10))
|
// .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).WithRepeatCount(10))
|
||||||
// TODO 自定义调度时间单位
|
// 自定义调度时间单位
|
||||||
.WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMinutes(1)).WithRepeatCount(10))
|
.WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMinutes(1)).WithRepeatCount(10))
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
@ -95,9 +90,9 @@ public class JobService : IJobService
|
||||||
.UsingJobData("trigger", "trigger值")
|
.UsingJobData("trigger", "trigger值")
|
||||||
.WithIdentity("testTrigger", "测试发出器")
|
.WithIdentity("testTrigger", "测试发出器")
|
||||||
.StartNow()
|
.StartNow()
|
||||||
// TODO StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 0)):触发器从每天的20:00(晚上8点)开始。
|
// StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 0)):触发器从每天的20:00(晚上8点)开始。
|
||||||
// TODO EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0)):触发器在每天的22:00(晚上10点)结束。
|
// EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0)):触发器在每天的22:00(晚上10点)结束。
|
||||||
// TODO WithIntervalInSeconds(2):在开始和结束时间之间,触发器将每2秒触发一次作业。
|
// WithIntervalInSeconds(2):在开始和结束时间之间,触发器将每2秒触发一次作业。
|
||||||
.WithDailyTimeIntervalSchedule(x => x.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(16, 0))
|
.WithDailyTimeIntervalSchedule(x => x.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(16, 0))
|
||||||
.EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0))
|
.EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0))
|
||||||
// 设置工作时间在每周的星期几
|
// 设置工作时间在每周的星期几
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class BaseConfig(WebApplicationBuilder builder)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// 配置跨域
|
// 配置跨域
|
||||||
UseCors();
|
UseCors();
|
||||||
// 配置日志相关
|
// 配置日志相关
|
||||||
// builder.Logging.AddLog4Net("Config/log4net.config");
|
// builder.Logging.AddLog4Net("Config/log4net.config");
|
||||||
|
@ -24,8 +24,11 @@ public class BaseConfig(WebApplicationBuilder builder)
|
||||||
// 设置文件最大上传大小
|
// 设置文件最大上传大小
|
||||||
options.Limits.MaxRequestBodySize = 1024 * 1024 * 100;
|
options.Limits.MaxRequestBodySize = 1024 * 1024 * 100;
|
||||||
});
|
});
|
||||||
// 添加Service服务
|
// 注入后台服务
|
||||||
builder.AddApplicationServices();
|
builder.AddApplicationServices();
|
||||||
|
// 设置控制器返回时间,统一格式
|
||||||
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
|
||||||
// 添加后台服务
|
// 添加后台服务
|
||||||
builder.AddApplicationBackendServices();
|
builder.AddApplicationBackendServices();
|
||||||
// 添加验证码
|
// 添加验证码
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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()));
|
||||||
|
```
|
Loading…
Reference in New Issue