EasyCore.UnitOfWork
8.3.4
dotnet add package EasyCore.UnitOfWork --version 8.3.4
NuGet\Install-Package EasyCore.UnitOfWork -Version 8.3.4
<PackageReference Include="EasyCore.UnitOfWork" Version="8.3.4" />
<PackageVersion Include="EasyCore.UnitOfWork" Version="8.3.4" />
<PackageReference Include="EasyCore.UnitOfWork" />
paket add EasyCore.UnitOfWork --version 8.3.4
#r "nuget: EasyCore.UnitOfWork, 8.3.4"
#:package EasyCore.UnitOfWork@8.3.4
#addin nuget:?package=EasyCore.UnitOfWork&version=8.3.4
#tool nuget:?package=EasyCore.UnitOfWork&version=8.3.4
🏗️ EasyCore.EFCoreRepository
English README | MongoDb English README
📚 概述 Repository 是软件开发中的一个重要概念,尤其在 领域驱动设计(DDD) 🎯 和 数据访问层 中广泛使用。Repository 是一种 抽象数据访问层的设计模式 🏛️,它封装了数据访问逻辑,使上层业务逻辑与底层数据存储解耦。Repository 就像是数据仓库的接口 📦,用于管理实体对象的持久化(增删改查)操作。
🚀 快速开始
1. 📝 Program 注册
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.EasyCoreDependencie();
builder.Services.AddDbContext<TestDbContext>();
// ✨ 使用 EasyCore EFCore Repository
builder.Services.AddEasyCoreEFCoreRepository();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
2. 🏷️ 实体继承
EasyCore.EFCoreRepository 提供了一个功能丰富的实体基类 EasyCoreEntity,包含:
🔄 并发标记
🗑️ 软删除
🏢 多租户 ID
// 💡 注意:EasyCoreEntity<TKey> 中的泛型类型为表的主键类型
public class TestEntity : EasyCoreEntity<Guid>
{
public string Name { get; set; }
public int Age { get; set; }
}
3. 🔧 仓储类继承
EasyCore.EFCoreRepository 提供了完整的仓储抽象和实现:
仓储接口 📜
public interface ITestEntityRepository : IRepository<TestDbContext, TestEntity>, ITransientDependencie
{
// 💡 ITransientDependencie 为 EasyCore.Dependencie 中的自动注入标记
}
仓储实现 ⚙️
public class TestEntityRepository : EfCoreRepository<TestDbContext, TestEntity>, ITestEntityRepository
{
public TestEntityRepository(TestDbContext dbContext, IServiceProvider serviceProvider)
: base(dbContext, serviceProvider)
{
}
}
4.💡 使用仓储
4.1 🎯 基础 CRUD 操作
[Route("api/[controller]")]
[ApiController]
public class RepositoryController : ControllerBase
{
private readonly ITestEntityRepository _repository;
public RepositoryController(ITestEntityRepository repository) => _repository = repository;
// 🔍 查询
[HttpGet]
public async Task<TestEntity> Get()
{
return await _repository.GetFirstAsync(e => e.Name == "Test");
}
// ➕ 新增
[HttpPost]
public async Task Post()
{
await _repository.InsertAsync(new TestEntity {
Name = "Test",
Age = 10,
Id = Guid.NewGuid()
}, true);
}
// ✏️ 更新
[HttpPut]
public async Task Put()
{
var entity = await _repository.GetFirstAsync(e => e.Name == "Test");
entity.Age = 20;
await _repository.UpdateAsync(entity, true);
}
// 🗑️ 删除(软删除)
[HttpDelete]
public async Task Delete()
{
var entity = await _repository.GetFirstAsync(e => e.Age == 20);
entity.IsDeleted = true;
await _repository.UpdateAsync(entity, true);
}
}
4.2 📋 完整的 API 列表
EasyCore.EFCoreRepository 提供了丰富的 API 方法:
🔧 过滤器管理
EfCoreRepository<TDbContext, TEntity> AddFilter(Type filterType);
EfCoreRepository<TDbContext, TEntity> RemoveFilter(Type filterType);
➕ 插入操作
Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default);
TEntity Insert(TEntity entity, bool autoSave = false);
Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default);
void InsertMany(IEnumerable<TEntity> entities, bool autoSave = false);
✏️ 更新操作
Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default);
TEntity Update(TEntity entity, bool autoSave = false);
Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default);
void UpdateMany(IEnumerable<TEntity> entities, bool autoSave = false);
🗑️ 删除操作
Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default);
void Delete(TEntity entity, bool autoSave = false);
Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default);
void DeleteMany(IEnumerable<TEntity> entities, bool autoSave = false);
💾 保存操作
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
int SaveChanges();
🔍 查询操作
// 获取列表
Task<List<TEntity>> GetListAsync(CancellationToken cancellationToken = default);
List<TEntity> GetList();
Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
List<TEntity> GetList(Expression<Func<TEntity, bool>> predicate);
// 数量统计
Task<long> GetCountAsync(CancellationToken cancellationToken = default);
long GetCount();
Task<long> GetCountAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
long GetCount(Expression<Func<TEntity, bool>> predicate);
// 分页查询(可选排序)
Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, CancellationToken cancellationToken = default);
List<TEntity> GetPagedList(int skipCount, int maxResultCount);
Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, Expression<Func<TEntity, object>> orderBy, bool ascending = true, CancellationToken cancellationToken = default);
List<TEntity> GetPagedList(int skipCount, int maxResultCount, Expression<Func<TEntity, object>> orderBy, bool ascending = true);
// 单实体查询(GetFirstAsync 未找到时抛出异常;GetFirst / GetFirstOrDefaultAsync 返回 null)
Task<TEntity> GetFirstAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<TEntity?> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
TEntity? GetFirst(Expression<Func<TEntity, bool>> predicate);
⚡ 直接删除操作
// DeleteDirect* 绕过软删除/租户过滤器,物理删除
Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
void DeleteDirect(Expression<Func<TEntity, bool>> predicate);
void DeleteManyDirect(IEnumerable<TEntity> entities, bool autoSave = false);
Task DeleteManyDirectAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default);
5.🎛️ 高级功能
🎯 WhereIf 支持
EasyCore.EFCoreRepository 提供了智能的条件查询支持:
IQueryable<T>.WhereIf(xxx != null, x => x.xxx == xxx)
✨ 特性:只有当 xxx != null 条件满足时,才会执行后面的过滤条件,否则继续执行后续代码。
6. 🔍 数据过滤器
EasyCore.EFCoreRepository 内置了两个实用的数据过滤器:
🗑️ ISoftDeleteFilter - 软删除过滤器
🏢 ITenantFilter - 租户过滤器(有租户值时按 TenantId 过滤;无租户时只查 TenantId 为 null 的数据)
可通过 ITenantProvider 自定义租户来源(默认与 AspNetCore.Mvc ICurrentTenant 对齐:先 HttpContext.Items["TenantId"],再请求头 X-Tenant-Id)。
自定义过滤器示例 🎨:
public class CustomDataFilter : IDataFilter, ITransientDependencie
{
public IQueryable<TEntity> Apply<TEntity>(IQueryable<TEntity> query) where TEntity : class, IEntity
{
query = query.Where(e => ((e as TestEntity)!.Name == "Test"));
return query;
}
}
动态过滤器管理 ⚡:
_repository
.RemoveFilter(typeof(ITenantFilter)) // 🗑️ 移除租户过滤器
.RemoveFilter(typeof(ISoftDeleteFilter)) // 🗑️ 移除软删除过滤器
.AddFilter(typeof(CustomDataFilter)) // ➕ 添加自定义过滤器
.Delete(e => e.Name == "Test1", true); // 🎯 执行操作
🔄 EasyCore.UnitOfWork
🎯 工作单元模式
EasyCore.UnitOfWork 提供了 [SaveChanges] 特性,支持 接口 / 类 / 方法 / 动态 API / Controller / 事件处理器:
- 服务 / 事件处理器:Castle DynamicProxy(经 DI 接口代理调用生效)
- API 层:
IFilterFactory+ 接口特性 Convention(EasyCoreAppService / Controller);Controller 不走 Castle 代理,避免双写
非 MVC 场景请把特性标在实现类/方法上,并通过接口解析服务。本包使用 Castle.Core.AsyncInterceptor。
ControllerBase(含 Dynamic API AppService)走 MVC Filter 路径。
1. 📝 Program 注册
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.EasyCoreDependencie();
builder.Services.AddDbContext<TestDbContext>();
// ✨ 使用 EasyCore EFCore Repository
builder.Services.AddEasyCoreEFCoreRepository();
// 🔄 UnitOfWork:Castle DynamicProxy + MVC Filter
builder.Services.AddEasyCoreUnitOfWork();
// RegisterSaveChangesFor / 程序集扫描已废弃(织入在编译期完成)
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
2. 📜 抽象接口定义
public interface IUnitOfWorkTest : ITransientDependencie
{
/// <summary>
/// 🎯 测试实体工作单元
/// </summary>
Task<TestEntity> EntityUnitOfWork();
/// <summary>
/// 💰 测试事务工作单元
/// </summary>
Task<TestEntity> Transaction();
}
public interface IUnitOfWorkTest2 : ITransientDependencie
{
/// <summary>
/// 🎯 测试实体工作单元
/// </summary>
Task<TestEntity> EntityUnitOfWork();
}
3. 🏷️ SaveChangesAttribute 特性使用
方法级别使用 🎯:
public class UnitOfWorkTest : IUnitOfWorkTest
{
private readonly ITestEntityRepository _repository;
public UnitOfWorkTest(ITestEntityRepository repository) => _repository = repository;
[SaveChanges(typeof(TestDbContext))]
public Task<TestEntity> EntityUnitOfWork()
=> _repository.InsertAsync(new TestEntity { Name = "Test", Age = 10, Id = Guid.NewGuid() });
[SaveChanges(true, typeof(TestDbContext))] // 💰 启用事务
public Task<TestEntity> Transaction()
=> _repository.InsertAsync(new TestEntity { Name = "Test", Age = 10, Id = Guid.NewGuid() });
}
类级别使用 🏛️:
[SaveChanges(typeof(TestDbContext))]
public class UnitOfWorkTest2 : IUnitOfWorkTest2
{
private readonly ITestEntityRepository _repository;
public UnitOfWorkTest2(ITestEntityRepository repository) => _repository = repository;
public Task<TestEntity> EntityUnitOfWork()
=> _repository.InsertAsync(new TestEntity { Name = "Test", Age = 10, Id = Guid.NewGuid() });
}
💡 特性参数说明:
第一个参数:是否为数据库事务,为 true 时执行事务保存 💰
第二个参数:指定要保存的数据库 DbContext 对象
🔍 EasyCore.EntityChange
📊 实体变更追踪
EasyCore.EntityChange 提供了强大的实体变更追踪能力!🕵️
1. 📝 Program 注册
// 1) 注册 EntityChange(默认扫描全部 Handler)
builder.Services.AddEasyCoreEntityChange();
// 可选:关掉扫描,只精准注册
// builder.Services.AddEasyCoreEntityChange(enableAssemblyScanning: false)
// .AddHandler<EntityChange>();
// 2) 在每个需要追踪的 AddDbContext 里显式挂拦截器(多 DbContext 就分别写)
builder.Services.AddDbContext<TestDbContext>((sp, options) =>
{
options.UseEasyCoreEntityChange(sp);
});
// builder.Services.AddDbContext<OtherDbContext>((sp, options) =>
// {
// options.UseSqlServer("...");
// options.UseEasyCoreEntityChange(sp); // 需要才挂
// });
builder.Services.AddEasyCoreEFCoreRepository();
说明:推荐在
AddDbContext((sp, options) => …)里调用UseEasyCoreEntityChange(sp),多个 DbContext 可按需分别启用。若漏写,与仓储联用时AddEasyCoreEFCoreRepository/AddEasyCoreMongoDbRepository仍会尝试自动挂上。软删除(IsDeleted从 false→true)按 Deleted 派发。Handler 异常默认向上抛出(可用Configure(o => o.SuppressHandlerExceptions = true)改为吞掉)。
2. 🎯 使用实体变更追踪
public class EntityChange :
IEntityUpdatedChangeHandler<TestEntity>,
IEntityDeletedChangeHandler<TestEntity>,
IEntityAddedChangeHandler<TestEntity>
{
private readonly ILogger<EntityChange> _logger;
public EntityChange(ILogger<EntityChange> logger) => _logger = logger;
// ➕ 实体新增处理
public async Task OnAddedAsync(TestEntity entity)
{
_logger.LogInformation($"🆕 实体新增: Id:{entity.Id}; Name:{entity.Name}; Age:{entity.Age};");
await Task.CompletedTask;
}
// 🗑️ 实体删除处理
public async Task OnDeletedAsync(TestEntity entity)
{
_logger.LogInformation($"🗑️ 实体删除: Id:{entity.Id}; Name:{entity.Name}; Age:{entity.Age};");
await Task.CompletedTask;
}
// ✏️ 实体更新处理
public Task OnUpdatedAsync(TestEntity oldEntity, TestEntity currentEntity)
{
_logger.LogInformation($"✏️ 实体更新: " +
$"Id:{oldEntity.Id} → {currentEntity.Id}; " +
$"Name:{oldEntity.Name} → {currentEntity.Name}; " +
$"Age:{oldEntity.Age} → {currentEntity.Age};");
return Task.CompletedTask;
}
}
🎯 支持的变更接口:
IEntityAddedChangeHandler<TEntity> - 实体新增处理器 ➕
IEntityDeletedChangeHandler<TEntity> - 实体删除处理器 🗑️
IEntityUpdatedChangeHandler<TEntity> - 实体更新处理器 ✏️
💎 Custom Entity 和 Custom Data Filter
1. 用户自定义数据库实体🦄
用户可根据自身项目进行自定义用户实体配置
public class CustomEntity : EasyCoreEntity<Guid>
{
public string CreateId{ get; set; }
}
public class TestCustomEntity : CustomEntity
{
}
CustomEntity为用户自定义实体对象,自定义实体中存在CreateId字段。保存时可用IEntityAddedChangeHandler<TEntity> 自动保存当前用户id。
public class TestCustomEntityRepository : EfCoreRepository<TestDbContext, TestCustomEntity>,ITestCustomEntityRepository,IEntityAddedChangeHandler<TestCustomEntity>
{
public TestCustomEntityRepository(TestDbContext dbContext, IServiceProvider serviceProvider) : base(dbContext, serviceProvider)
{
}
public async Task OnAddedAsync(TestCustomEntity entity)
{
if (entity is CustomEntity customEntity)
{
customEntity.CreateId = "Test";
}
await Task.CompletedTask;
}
}
2. 用户自定义数据过滤器🎁
用户可根据自身项目进行自定义数据过滤器配置
public class TestEntityRepository :
EfCoreRepository<TestDbContext, TestEntity>,
ITestEntityRepository
{
public TestEntityRepository(TestDbContext dbContext, IServiceProvider serviceProvider) : base(dbContext, serviceProvider)
{
}
/// <summary>
/// Applies permanent data filters before persisting entities (Insert/Update/Delete).
/// This method is called during the persistence pipeline to enforce global filters.
/// </summary>
/// <param name="dataFilters">The list of data filters that are currently scheduled for execution.</param>
/// <returns>The updated list of data filters after applying permanent filter rules.</returns>
public override List<IDataFilter> OnApplyPersistingFilters(List<IDataFilter> dataFilters)
{
AddOnce(dataFilters, typeof(CustomDataFilter));
RemoveIfExistsFilter(dataFilters, typeof(CustomDataFilter));
return dataFilters;
}
}
重写OnApplyPersistingFilters方法,添加或修改永久过滤器设置。
✨ 特性:当实体完成增删改操作时,系统会自动调用对应的接口方法,实现无缝的变更追踪!
🏷️CustomRepository
用户自定义仓储,由于每一个系统的实体字段都不相同。EasyCore不会对用户的实体字段和字段类型进行限制,比如添加人id等信息。类型可以是Guid也可以是long。
CustomEntityRepository 提供了三个重写方法:OnBeforeAdd (在添加前);OnBeforeUpdate (在修改前);OnBeforeDelete (在删除前);
这样我们在增删改的时候不需要手动的添加字段值。CustomRepository自动添加。
public class CustomEntityRepository<TDbContext, TEntity> : EfCoreRepository<TDbContext, TEntity>
where TDbContext : DbContext
where TEntity : class, IEntity
{
public CustomEntityRepository(TDbContext dbContext, IServiceProvider serviceProvider) : base(dbContext, serviceProvider)
{
}
/// <summary>
/// Custom method to set the CreateId property of the entity before adding it to the database.
/// </summary>
/// <param name="entity"></param>
public override void OnBeforeAdd(TEntity entity)
{
if (entity is CustomEntity customEntity)
{
customEntity.CreateId = "Test";
}
}
/// <summary>
/// Custom method to set the UpdateId property of the entity before updating it in the database.
/// </summary>
/// <param name="entity"></param>
public override void OnBeforeUpdate(TEntity entity)
{
// Do something entity before update
}
/// <summary>
/// Custom method to set the DeleteId property of the entity before deleting it from the database.
/// </summary>
/// <param name="entity"></param>
public override void OnBeforeDelete(TEntity entity)
{
// Do something entity before delete
}
public class TestCustomEntityRepository : CustomEntityRepository<TestDbContext, TestCustomEntity>, ITestCustomEntityRepository
{
public TestCustomEntityRepository(TestDbContext dbContext, IServiceProvider serviceProvider) : base(dbContext, serviceProvider)
{
}
}
把选择权完全交由用户处理。
🎉 总结
EasyCore.EFCoreRepository 系列组件提供了:
🏗️ 完整的仓储模式实现
🔄 智能的工作单元管理
🔍 强大的实体变更追踪
🎯 丰富的查询和过滤功能
⚡ 高性能的数据访问
让您的数据访问层更加 优雅、强大、易维护!✨
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Castle.Core.AsyncInterceptor (>= 2.1.0)
- Microsoft.EntityFrameworkCore (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.