✨ feat(新增): EFCore做逻辑删除
This commit is contained in:
parent
04f0c79c2e
commit
4c8672a42b
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
// 使用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;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,5 @@ public class BaseEntity
|
|||
|
||||
public long UpdateUserId { get; set; }
|
||||
|
||||
public string? OperationMessage { get; set; }
|
||||
|
||||
public bool IsDeleted { get; set; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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))
|
||||
// 设置工作时间在每周的星期几
|
||||
|
|
|
@ -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.
|
@ -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